嵌入式学习-QT-Day09

嵌入式学习-QT-Day09

九、网络

1、基础概念

[1.1 TCP/UDO](#1.1 TCP/UDO)

[1.2 IP地址与端口号](#1.2 IP地址与端口号)

2、准备工作

3、相关函数

九、网络

1、基础概念

1.1 TCP/UDP

TCP/UDP

UDP TCP 协议相同点:都存在于传输层,全双工通信

TCP:全双工通信、面向连接、可靠

TCP(即传输控制协议):是一种面向连接的传输层协议,它能提供高可靠性通信(即数据无误、数据无丢失、数据无失序、数据无重复到达的通信)。

高可靠原因:1. 三次握手、四次挥手

  1. 序列号和应答机制

  2. 超时,错误重传机制

  3. 拥塞控制、流量控制(滑动窗口)

适用场景

适合于对传输质量要求较高的通信

在需要可靠数据传输的场合,通常使用TCP协议

MSN/QQ等即时通讯软件的用户登录账户管理相关的功能通常采用TCP协议

UDP:全双工通信、面向无连接、不可靠

UDP(User Datagram Protocol)用户数据报协议,是不可靠的无连接的协议。在数据发送前,因为不需要进行连接,所以可以进行高效率的数据传输。

适用场景

发送小尺寸数据(如对DNS服务器进行IP地址查询时)

适合于广播/组播式通信中。

MSN/QQ/Skype等即时通讯软件的点对点文本通讯以及音视频通讯通常采用UDP协议

1.2 IP地址与端口号

IP

●IP地址是Internet中主机的标识

●Internet中的主机要与别的机器通信必须具有一个IP地址

●IP地址为32位(IPv4)或者128位(IPv6)

●每个数据包都必须携带目的IP地址和源IP地址,路由器依靠此信息为数据包选择路由

●表示形式:常用点分十进制形式,如202.38.64.10,最后都会转换为一个32位的无符号整数。

端口号

●为了区分一台主机接收到的数据包应该转交给哪个进程来进行处理,使用端口号来区分

●TCP端口号与UDP端口号独立

●端口用两个字节来表示 2byte

端口:1~1023是被占用的。

动态或私有端口:49152~65535 --固定某些服务使用--

建议使用2000以上,非豹子号的端口号。本次授课使用8887。

2、准备工作

与数据库编程一样,Qt的网络功能需要在.pro项目配置文件中增加network模块。

客户端:

服务端:

客户端:

dialog.h

复制代码
#ifndef DIALOG_H`
`#define DIALOG_H`

`#include <QDialog>`

`namespace Ui {`
`class Dialog;`
`}`

`class Dialog : public QDialog`
`{`
`    Q_OBJECT`

`public:`
`    explicit Dialog(QWidget *parent = 0);`
`    ~Dialog();`

`private:`
`    Ui::Dialog *ui;`
    
`private slots:`
`    void btnConnectClickedSlot();`
`    void btnSendClickedSlot();`
`};`

`#endif // DIALOG_H`
`

dialog.cpp

复制代码
#include "dialog.h"`
`#include "ui_dialog.h"`

`Dialog::Dialog(QWidget *parent) :`
`    QDialog(parent),`
`    ui(new Ui::Dialog)`
`{`
`    ui->setupUi(this);`

`    connect(ui->pushButtonConnect,SIGNAL(clicked()),`
`            this,SLOT(btnConnectClickedSlot()));`

`    connect(ui->pushButtonSend,SIGNAL(clicked()),`
`            this,SLOT(btnSendClickedSlot()));`
`}`

`Dialog::~Dialog()`
`{`
`    delete ui;`
`}`

`void Dialog::btnConnectClickedSlot()`
`{`

`}`

`void Dialog::btnSendClickedSlot()`
`{`

`}

网络编程主要用到两个类:

  • QTcpServer

表示一个基于TCP的服务器,需要注意的是,此类直接继承QObject类,不继承QIODevice类,因此不具备任何的IO能力。

  • QTcpSocket

表示一个基于TCP的Socket连接,间接继承了QIODevice类,因此此类对象可以进行IO读写。

3、相关函数

服务器

复制代码
// 构造函数 堆内存开辟`
`QTcpServer::​QTcpServer(QObject * parent = 0)
复制代码
// 开启监听服务,等待客户端发起连接`
`// 参数1:监听来源(那个网段的IP地址),默认值表示不加任何限制`
`// 参数2:服务器所占用的端口号,默认值0表示随机选取。本次使用8887`
`bool QTcpServer::​listen(const QHostAddress & address = QHostAddress::Any, quint16 port = 0)
复制代码
// 判断监听是否已开启`
`bool QTcpServer::​isListening() const
复制代码
// 关闭`
`void QTcpServer::​close()

dialog.h

复制代码
#ifndef DIALOG_H`
`#define DIALOG_H`

`#include <QDialog>`
`#include <QTcpServer>`

`namespace Ui {`
`class Dialog;`
`}`

`class Dialog : public QDialog`
`{`
`    Q_OBJECT`

`public:`
`    explicit Dialog(QWidget *parent = 0);`
`    ~Dialog();`

`private:`
`    Ui::Dialog *ui;`
`    QTcpServer *server;`
`};`

`#endif // DIALOG_H`
`

dialog.cpp

复制代码
#include "dialog.h"`
`#include "ui_dialog.h"`

`Dialog::Dialog(QWidget *parent) :`
`    QDialog(parent),`
`    ui(new Ui::Dialog)`
`{`
`    ui->setupUi(this);`

`    server = new QTcpServer(this);`

`    // 开启监听`
`    bool result = server->listen(QHostAddress::Any,8887);`
`    if(!result)`
`    {`
`        ui->textBrowser->append("监听失败!!");`
`        return;`
`    }`
`    ui->textBrowser->append("监听开启成功,端口号为:8887");`
`}`

`Dialog::~Dialog()`
`{`
`    // 关闭监听`
`    if(server->isListening())`
`    {`
`        server->close();`
`    }`
`    delete ui;`
`}`
`

编写客户端:

复制代码
// 构造函数 堆区创建`
`QTcpSocket::​QTcpSocket(QObject * parent = 0)`
`
复制代码
// 连接到服务器`
`// 参数1:服务器的IP地址`
`// 参数2:服务器的端口号`
`// 参数3:打开模式`
`void QAbstractSocket::​connectToHost(`
    `const QString & hostName, `
    `quint16 port, `
    `OpenMode openMode = ReadWrite)[virtual]

dialog.h

复制代码
#ifndef DIALOG_H`
`#define DIALOG_H`

`#include <QDialog>`
`#include <QTcpSocket>`

`namespace Ui {`
`class Dialog;`
`}`

`class Dialog : public QDialog`
`{`
`    Q_OBJECT`

`public:`
`    explicit Dialog(QWidget *parent = 0);`
`    ~Dialog();`

`private:`
`    Ui::Dialog *ui;`
`    QTcpSocket *socket;`

`private slots:`
`    void btnConnectClickedSlot();`
`    void btnSendClickedSlot();`
`};`

`#endif // DIALOG_H`
`

dialog.cpp

复制代码
#include "dialog.h"`
`#include "ui_dialog.h"`

`Dialog::Dialog(QWidget *parent) :`
`    QDialog(parent),`
`    ui(new Ui::Dialog)`
`{`
`    ui->setupUi(this);`

`    connect(ui->pushButtonConnect,SIGNAL(clicked()),`
`            this,SLOT(btnConnectClickedSlot()));`

`    connect(ui->pushButtonSend,SIGNAL(clicked()),`
`            this,SLOT(btnSendClickedSlot()));`

`    socket = new QTcpSocket(this);`
`}`

`Dialog::~Dialog()`
`{`
`    // 如果数据流处于打开状态`
`    if(socket->isOpen())`
`    {`
`        // 如果打开则关闭`
`        socket->close();`
`    }`
`    delete ui;`
`}`

`void Dialog::btnConnectClickedSlot()`
`{`
`    QString ip = ui->lineEditIp->text();`
`    int port = ui->spinBox->value();`

`    // 建立连接`
`    socket->connectToHost(ip,port);`
`}`

`void Dialog::btnSendClickedSlot()`
`{`

`}`
`

再回到服务器:

我们怎么直到,是否有客户端连接了那?

所有通知类的,我们应该第一时间想到的就是信号槽。

复制代码
// 每当有新的连接可用时,就会发出此信号`
`void QTcpServer::​newConnection()[signal]

dialog.h

复制代码
#ifndef DIALOG_H`
`#define DIALOG_H`

`#include <QDialog>`
`#include <QTcpServer>`

`namespace Ui {`
`class Dialog;`
`}`

`class Dialog : public QDialog`
`{`
`    Q_OBJECT`

`public:`
`    explicit Dialog(QWidget *parent = 0);`
`    ~Dialog();`

`private:`
`    Ui::Dialog *ui;`
`    QTcpServer *server;`

`private slots:`
`    void newConnectionSlot();`
`};`

`#endif // DIALOG_H`
`

dialog.cpp

复制代码
#include "dialog.h"`
`#include "ui_dialog.h"`

`Dialog::Dialog(QWidget *parent) :`
`    QDialog(parent),`
`    ui(new Ui::Dialog)`
`{`
`    ui->setupUi(this);`

`    server = new QTcpServer(this);`

`    // 开启监听`
`    bool result = server->listen(QHostAddress::Any,8887);`
`    if(!result)`
`    {`
`        ui->textBrowser->append("监听失败!!");`
`        return;`
`    }`
`    connect(server,SIGNAL(newConnection()),`
`            this,SLOT(newConnectionSlot()));`
`    ui->textBrowser->append("监听开启成功,端口号为:8887");`
`}`

`Dialog::~Dialog()`
`{`
`    // 关闭监听`
`    if(server->isListening())`
`    {`
`        server->close();`
`    }`
`    delete ui;`
`}`

`void Dialog::newConnectionSlot()`
`{`
`    ui->textBrowser->append("新连接来啦!!!!");`
`}`
`

客户端:

复制代码
// 连接成功的信号`
`void QAbstractSocket::​connected()[signal]
复制代码
// 断开连接的信号`
`void QAbstractSocket::​disconnected()[signal]

dialog.cpp

复制代码
#include "dialog.h"`
`#include "ui_dialog.h"`

`Dialog::Dialog(QWidget *parent) :`
`    QDialog(parent),`
`    ui(new Ui::Dialog)`
`{`
`    ui->setupUi(this);`

`    connect(ui->pushButtonConnect,SIGNAL(clicked()),`
`            this,SLOT(btnConnectClickedSlot()));`

`    connect(ui->pushButtonSend,SIGNAL(clicked()),`
`            this,SLOT(btnSendClickedSlot()));`

`    socket = new QTcpSocket(this);`
`}`

`Dialog::~Dialog()`
`{`
`    // 如果数据流处于打开状态`
`    if(socket->isOpen())`
`    {`
`        // 如果打开则关闭`
`        socket->close();`
`    }`
`    delete ui;`
`}`

`void Dialog::btnConnectClickedSlot()`
`{`
`    QString ip = ui->lineEditIp->text();`
`    int port = ui->spinBox->value();`

`    // 建立连接`
`    socket->connectToHost(ip,port);`

`    connect(socket,SIGNAL(connected()),`
`            this,SLOT(connectedSlot()));`

`    connect(socket,SIGNAL(disconnected()),`
`            this,SLOT(disConnectedSlot()));`
`}`

`void Dialog::btnSendClickedSlot()`
`{`

`}`


`// 连接成功`
`void Dialog::connectedSlot()`
`{`
`    // 屏蔽连接按钮`
`    ui->pushButtonConnect->setEnabled(false);`
`    ui->pushButtonConnect->setText("已连接");`

`    // 释放发送按钮`
`    ui->pushButtonSend->setEnabled(true);`
`}`

`// 断开连接`
`void Dialog::disConnectedSlot()`
`{`
`    // 释放连接按钮`
`    ui->pushButtonConnect->setEnabled(true);`
`    ui->pushButtonConnect->setText("连接");`

`    // 屏蔽发送按钮`
`    ui->pushButtonSend->setEnabled(false);`
`}
复制代码
// 返回与客户端连接的QTcpSocket对象`
`QTcpSocket * QTcpServer::​nextPendingConnection()[virtual]
复制代码
// 获取对面(客户端)的IP地址`
`// 返回值为IP地址的封装类`
`QHostAddress QAbstractSocket::​peerAddress() const
复制代码
// 转换为IP地址字符串,在有的计算机中会自动增加一个前缀`
`QString QHostAddress::​toString() const
复制代码
// 返回对面的(客户端)端口号`
`quint16 QAbstractSocket::​peerPort() const

dialog.h

复制代码
#ifndef DIALOG_H`
`#define DIALOG_H`

`#include <QDialog>`
`#include <QTcpServer>`
`#include <QTcpSocket>`

`namespace Ui {`
`class Dialog;`
`}`

`class Dialog : public QDialog`
`{`
`    Q_OBJECT`

`public:`
`    explicit Dialog(QWidget *parent = 0);`
`    ~Dialog();`

`private:`
`    Ui::Dialog *ui;`
`    QTcpServer *server;`
`    QTcpSocket *socket = NULL;  // 通信对象(这样只能存一个)`

`private slots:`
`    void newConnectionSlot();`
`    void disconnectedSlot();`
`};`

`#endif // DIALOG_H`
`

dialog.cpp

复制代码
#include "dialog.h"`
`#include "ui_dialog.h"`

`Dialog::Dialog(QWidget *parent) :`
`    QDialog(parent),`
`    ui(new Ui::Dialog)`
`{`
`    ui->setupUi(this);`

`    server = new QTcpServer(this);`

`    // 开启监听`
`    bool result = server->listen(QHostAddress::Any,8887);`
`    if(!result)`
`    {`
`        ui->textBrowser->append("监听失败!!");`
`        return;`
`    }`
`    connect(server,SIGNAL(newConnection()),`
`            this,SLOT(newConnectionSlot()));`
`    ui->textBrowser->append("监听开启成功,端口号为:8887");`
`}`

`Dialog::~Dialog()`
`{`
`    // 关闭监听`
`    if(server->isListening())`
`    {`
`        server->close();`
`    }`
`    delete ui;`
`}`

`void Dialog::newConnectionSlot()`
`{`
`    // 如果不是第一次连接,就踢掉上一个人`
`    if(socket != NULL)`
`    {`
`        // 踢掉上一个人`
`        socket->close();`
`    }`

`    // 保存当前连接对象`
`    socket = server->nextPendingConnection();`

`    // 断开检测信号槽`
`    connect(socket,SIGNAL(disconnected()),`
`            this,SLOT(disconnectedSlot()));`

`    // 获取对面的ip地址`
`    QString ip = socket->peerAddress().toString();`
`    // 获取对面的端口号`
`    quint16 port = socket->peerPort();`

`    // 字符串拼接`
`    ip.prepend("新连接来了!").append(":").append(QString::number(port));`

`    ui->textBrowser->append(ip);`
`}`

`void Dialog::disconnectedSlot()`
`{`
`    ui->textBrowser->append("老连接走了!!");`
`}`
`

数据流发送与处理的第一种方式:

复制代码
// 构造函数`
`// 参数是Qt的读写类,可以是QFile,也可以是QTcpSocket。。。。。。`
`QTextStream::​QTextStream(QIODevice * device)
复制代码
// 输出字符串内容,支持链式调用`
`QTextStream &	operator<<(const QString & string)
复制代码
// 有数据可读时发射`
`void QIODevice::​readyRead()[SIGNAL]
复制代码
// 读取最大长度为maxlen个QChar的内容,返回值为读取的字符串`
`QString QTextStream::​read(qint64 maxlen)
复制代码
// 读取所有字符`
`QString QTextStream::​readAll()
复制代码
// 一次读取一行文本`
`// 参数为一行文本的最大字符数`
`QString QTextStream::​readLine(qint64 maxlen = 0)

客户端:

dialog.h

复制代码
#ifndef DIALOG_H`
`#define DIALOG_H`

`#include <QDialog>`
`#include <QTcpSocket>`
`#include <QTextStream>  // 文本流类`
`#include <QMessageBox>`

`namespace Ui {`
`class Dialog;`
`}`

`class Dialog : public QDialog`
`{`
`    Q_OBJECT`

`public:`
`    explicit Dialog(QWidget *parent = 0);`
`    ~Dialog();`

`private:`
`    Ui::Dialog *ui;`
`    QTcpSocket *socket;`

`private slots:`
`    void btnConnectClickedSlot();`
`    void btnSendClickedSlot();`
`    void connectedSlot();`
`    void disConnectedSlot();`
`};`

`#endif // DIALOG_H`
`

dialog.cpp

复制代码
#include "dialog.h"`
`#include "ui_dialog.h"`

`Dialog::Dialog(QWidget *parent) :`
`    QDialog(parent),`
`    ui(new Ui::Dialog)`
`{`
`    ui->setupUi(this);`

`    connect(ui->pushButtonConnect,SIGNAL(clicked()),`
`            this,SLOT(btnConnectClickedSlot()));`

`    connect(ui->pushButtonSend,SIGNAL(clicked()),`
`            this,SLOT(btnSendClickedSlot()));`

`    socket = new QTcpSocket(this);`
`}`

`Dialog::~Dialog()`
`{`
`    // 如果数据流处于打开状态`
`    if(socket->isOpen())`
`    {`
`        // 如果打开则关闭`
`        socket->close();`
`    }`
`    delete ui;`
`}`

`void Dialog::btnConnectClickedSlot()`
`{`
`    QString ip = ui->lineEditIp->text();`
`    int port = ui->spinBox->value();`

`    // 建立连接`
`    socket->connectToHost(ip,port);`

`    connect(socket,SIGNAL(connected()),`
`            this,SLOT(connectedSlot()));`

`    connect(socket,SIGNAL(disconnected()),`
`            this,SLOT(disConnectedSlot()));`
`}`

`void Dialog::btnSendClickedSlot()`
`{`
`    QString text = ui->lineEditSend->text();`
`    if(text == "")`
`    {`
`        QMessageBox::information(this,"提示","请输入发送的内容!!");`
`        return;`
`    }`

`    // QTextStream文本流(方法一)`
`    // QTextStream直接使用Unicode编码,适合Qt和Qt之间通信`
`    // 可以简化文本的读写操作`
`    QTextStream output(socket);`
`    output << text;`
`}`


`// 连接成功`
`void Dialog::connectedSlot()`
`{`
`    // 屏蔽连接按钮`
`    ui->pushButtonConnect->setEnabled(false);`
`    ui->pushButtonConnect->setText("已连接");`

`    // 释放发送按钮`
`    ui->pushButtonSend->setEnabled(true);`
`}`

`// 断开连接`
`void Dialog::disConnectedSlot()`
`{`
`    // 释放连接按钮`
`    ui->pushButtonConnect->setEnabled(true);`
`    ui->pushButtonConnect->setText("连接");`

`    // 屏蔽发送按钮`
`    ui->pushButtonSend->setEnabled(false);`
`}`
`

服务端

dialog.h

复制代码
#ifndef DIALOG_H`
`#define DIALOG_H`

`#include <QDialog>`
`#include <QTcpServer>`
`#include <QTcpSocket>`
`#include <QTextStream>  // 文本流`

`namespace Ui {`
`class Dialog;`
`}`

`class Dialog : public QDialog`
`{`
`    Q_OBJECT`

`public:`
`    explicit Dialog(QWidget *parent = 0);`
`    ~Dialog();`

`private:`
`    Ui::Dialog *ui;`
`    QTcpServer *server;`
`    QTcpSocket *socket = NULL;  // 通信对象(这样只能存一个)`

`private slots:`
`    void newConnectionSlot();`
`    void disconnectedSlot();`
`    void readyReadSlot();`
`};`

`#endif // DIALOG_H`
`

dialog.cpp

复制代码
#include "dialog.h"`
`#include "ui_dialog.h"`

`Dialog::Dialog(QWidget *parent) :`
`    QDialog(parent),`
`    ui(new Ui::Dialog)`
`{`
`    ui->setupUi(this);`

`    server = new QTcpServer(this);`

`    // 开启监听`
`    bool result = server->listen(QHostAddress::Any,8887);`
`    if(!result)`
`    {`
`        ui->textBrowser->append("监听失败!!");`
`        return;`
`    }`
`    connect(server,SIGNAL(newConnection()),`
`            this,SLOT(newConnectionSlot()));`
`    ui->textBrowser->append("监听开启成功,端口号为:8887");`
`}`

`Dialog::~Dialog()`
`{`
`    // 关闭监听`
`    if(server->isListening())`
`    {`
`        server->close();`
`    }`
`    delete ui;`
`}`

`void Dialog::newConnectionSlot()`
`{`
`    // 如果不是第一次连接,就踢掉上一个人`
`    if(socket != NULL)`
`    {`
`        // 踢掉上一个人`
`        socket->close();`
`    }`

`    // 保存当前连接对象`
`    socket = server->nextPendingConnection();`

`    // 断开检测信号槽`
`    connect(socket,SIGNAL(disconnected()),`
`            this,SLOT(disconnectedSlot()));`

`    // 连接数据流处理信号槽`
`    connect(socket,SIGNAL(readyRead()),`
`            this,SLOT(readyReadSlot()));`

`    // 获取对面的ip地址`
`    QString ip = socket->peerAddress().toString();`
`    // 获取对面的端口号`
`    quint16 port = socket->peerPort();`

`    // 字符串拼接`
`    ip.prepend("新连接来了!").append(":").append(QString::number(port));`

`    ui->textBrowser->append(ip);`
`}`

`void Dialog::disconnectedSlot()`
`{`
`    ui->textBrowser->append("老连接走了!!");`
`}`

`void Dialog::readyReadSlot()`
`{`
`    // 创建文本流对象`
`    QTextStream input(socket);`

`    // 读取内容`
`    QString text = input.read(128);`

`    // 显示`
`    ui->textBrowser->append(text);`
`}`
`

第二种方式:

客户端

dialog.cpp

服务器:

dialog.cpp

相关推荐
百流2 分钟前
scala基础学习(数据类型)-数组
开发语言·学习·scala
Zilliz Planet27 分钟前
Milvus×EasyAi:如何用java从零搭建人脸识别应用
java·开发语言·milvus
天天进步201533 分钟前
Java全栈项目 - 汽车维修服务管理平台
java·开发语言·汽车
虾球xz42 分钟前
游戏引擎学习第61天
java·学习·游戏引擎
qq_430583971 小时前
QT笔记- QTreeView + QFileSystemModel 当前位置的保存与恢复 #选中 #保存当前索引
开发语言·笔记·qt
Crossoads1 小时前
【汇编语言】外中断(一)—— 外中断的魔法:PC机键盘如何触发计算机响应
android·开发语言·数据库·深度学习·机器学习·计算机外设·汇编语言
Zik----1 小时前
Anaconda搭建Python虚拟环境并在Pycharm中配置(小白也能懂)
开发语言·人工智能·python·机器学习·pycharm
凯子坚持 c1 小时前
解锁仓颉编程语言的奥秘:枚举类型、模式匹配与类接口全解析
开发语言·华为·harmonyos
小王爱吃月亮糖1 小时前
QT-QVariant类应用
开发语言·c++·笔记·qt·visual studio