【QT】QT中的网络编程(TCP 和 UDP通信)

QT中的网络编程(TCP 和 UDP通信)

  • 1.tcp
    • [1.1 tcp通信](#1.1 tcp通信)
      • [1.1.1 相比linux中tcp通信:](#1.1.1 相比linux中tcp通信:)
      • [1.1.2 QT中的tcp通信:](#1.1.2 QT中的tcp通信:)
    • [1.2 tcp通信流程](#1.2 tcp通信流程)
      • [1.2.1 服务器流程:](#1.2.1 服务器流程:)
        • [1.2.1.1 示例代码](#1.2.1.1 示例代码)
        • [1.2.1.2 现象](#1.2.1.2 现象)
      • [1.2.2 客户端流程:](#1.2.2 客户端流程:)
        • [1.2.2.1 示例代码](#1.2.2.1 示例代码)
        • [1.2.2.2 现象:](#1.2.2.2 现象:)
    • [1.3 获取对方的ip和端口号](#1.3 获取对方的ip和端口号)
      • [1.3.1 示例代码(同服务器客户端代码)](#1.3.1 示例代码(同服务器客户端代码))
    • [1.4 多个客户端连接服务器](#1.4 多个客户端连接服务器)
    • [1.5 判断对方断开连接](#1.5 判断对方断开连接)
      • [1.5.1 示例代码:](#1.5.1 示例代码:)
  • [2. udp](#2. udp)
    • [2.1 udp通信流程](#2.1 udp通信流程)
      • [2.1.1 发送端:](#2.1.1 发送端:)
        • [2.1.1.1 示例代码:](#2.1.1.1 示例代码:)
      • [2.1.2 接收端:](#2.1.2 接收端:)
        • [2.1.2.1 示例代码](#2.1.2.1 示例代码)
        • [2.1.2.2 现象:](#2.1.2.2 现象:)
    • [2.2 bind需要注意的问题](#2.2 bind需要注意的问题)

注意:使用QT中的网络编程,必须在.pro文件中添加 QT += network

1.tcp

1.1 tcp通信

1.1.1 相比linux中tcp通信:

服务器: socket --》bind --》listen --》accept --》read/write --》close

客户端: socket --》bind --》connect --》read/write --》close

1.1.2 QT中的tcp通信:

涉及到两个类:

QTcpServer --》表示服务器

QTcpSocket --》表示套接字

1.2 tcp通信流程

1.2.1 服务器流程:

cpp 复制代码
第一步: 创建QTcpServer的对象,调用listen方法(绑定ip和端口号,顺便监听)
      //构造函数
      QTcpServer::QTcpServer(QObject *parent = Q_NULLPTR)
              参数:parent --》this指针

      bool QTcpServer::listen(const QHostAddress &address = QHostAddress::Any, quint16 port = 0)
            返回值:成功 true 失败 false
              参数:address --》要绑定的ip地址
                    QHostAddress::QHostAddress(const QString &address)
                                  参数:address --》要绑定的ip地址
                    port --》要绑定的端口号
第二步:如果有新的客户端连接服务器,QTcpServer的对象会自动触发newConnection信号,程序员必须关联这个信号,在自定义的槽函数中实现代码逻辑(产生跟这个客户端对应的套接字)
      [signal] void QTcpServer::newConnection()
      QTcpSocket *QTcpServer::nextPendingConnection()
           返回值: QTcpSocket *目前连接成功的客户端套接字
第三步:使用刚才产生的那个套接字跟客户端通信
      发送信息:qint64 QTcpSocket::write(const QByteArray &byteArray)
      接收信息:QByteArray QTcpSocket::readAll()
               QByteArray QTcpSocket::read(qint64 maxSize)
           注意:如果对方有发送信息过来,不能直接调用read/readAll接收
                 如果对方有发送信息过来,QTcpSocket的对象会自动触发readyRead()信号,程序员在自定义的槽函数里面调用read/readAll接收信息即可
第四步:关闭套接字
      close()

图示:

1.2.1.1 示例代码
cpp 复制代码
// widget.h
#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>
#include <QTcpServer>
#include <QTcpSocket>
#include <QHostAddress>

QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE

class Widget : public QWidget
{
    Q_OBJECT

public:
    Widget(QWidget *parent = nullptr);
    ~Widget();

public slots:
    void newLink();
    void recvClientMsg();
    void clientUnlink();

private slots:
    void on_pushButton_clicked();

private:
    Ui::Widget *ui;
    QTcpServer *TcpServer;
    QTcpSocket *newTcpSocket;
};
#endif // WIDGET_H

// widget.cpp
#include "widget.h"
#include "ui_widget.h"

Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);
    // 给主窗口设置标题
    this->setWindowTitle("服务器");

    // 1.初始化QTcpServer对象
    TcpServer = new QTcpServer(this);
    // 2.绑定并监听
    TcpServer->listen(QHostAddress("192.168.10.7"), 10000);
    // 3.当有新的客户端连接时,会长生newConnection信号,需要主动关联newConnection信号
    connect(TcpServer, &QTcpServer::newConnection, this, &Widget::newLink);
}

Widget::~Widget()
{
    delete ui;
}

// 跟newConnection信号对应的槽函数
void Widget::newLink()
{
    qDebug()<<"有客户端连接到服务器";
    // 调用nextPendingConnection()产生新的套接字
    newTcpSocket = TcpServer->nextPendingConnection();

    //获取对方(客户端)的ip和端口号
    QString clientIP = newTcpSocket->peerAddress().toString();
    quint16 clientPort = newTcpSocket->peerPort();
    //拼接ip和端口号在标签上显示
    QString clientIPAndPort = QString("%1@%2").arg(clientIP).arg(clientPort);
    ui->label->setText(clientIPAndPort);
    //在横向列表框中显示
    ui->listWidget->addItem(clientIPAndPort);
    // 关联readyRead信号,在槽函数中接收对方发送过来的信息
    connect(newTcpSocket, &QIODevice::readyRead, this, &Widget::recvClientMsg);
    // 关联disconnected()信号,判断客户端是否断开连接
    connect(newTcpSocket, &QAbstractSocket::disconnected, this, &Widget::clientUnlink);
}

//发送信息的槽函数
void Widget::on_pushButton_clicked()
{
    // 获取文本编辑框中输入的内容
    QString serverSendInfo = ui->textEdit->toPlainText();
    // 4.把内容发送给客户端
    newTcpSocket->write(serverSendInfo.toUtf8());
}

//接收客户端信息
void Widget::recvClientMsg()
{
    // 在套接字中读取到的信息
    QByteArray clientMsg = newTcpSocket->readAll();
    //在文本浏览框中显示出来
    ui->textBrowser->setText(clientMsg);
}

// 客户端断开连接
void Widget::clientUnlink()
{
    qDebug() << "客户端断开连接";
}
1.2.1.2 现象

1.2.2 客户端流程:

cpp 复制代码
第一步:创建QTcpSocket的对象,调用bind函数绑定ip和端口号
      //构造函数
      QTcpSocket::QTcpSocket(QObject *parent = nullptr)
             参数:parent --》this指针
      bool QAbstractSocket::bind(const QHostAddress &address, quint16 port = 0)
           返回值: 成功 true 失败 false
             参数: address --》要绑定的ip地址
                   port --》要绑定的端口号
第二步:连接服务器
       void QAbstractSocket::connectToHost(const QHostAddress &address, quint16 port)
             参数:address --》服务器的ip地址
                   port --》服务器的端口号
第三步:使用刚才新建的那个套接字跟服务器通信
      发送信息:qint64 QTcpSocket::write(const QByteArray &byteArray)
      接收信息:QByteArray QTcpSocket::readAll()
                QByteArray QTcpSocket::read(qint64 maxSize)
           注意:如果对方有发送信息过来,不能直接调用read/readAll接收
                 如果对方有发送信息过来,QTcpSocket的对象会自动触发readyRead(),程序员在自定义的槽函数里面调用read/readAll接收信息即可
第四步:关闭套接字
      close()

图示:

1.2.2.1 示例代码
cpp 复制代码
// widget.h
#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>
#include <QTcpSocket>
#include <QHostAddress>

QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE

class Widget : public QWidget
{
    Q_OBJECT

public:
    Widget(QWidget *parent = nullptr);
    ~Widget();

private slots:
    void on_btn_connectServer_clicked();
    void on_btn_sendMsgServer_clicked();
    void recvServerMsg();
    void serverUnlink();

private:
    Ui::Widget *ui;
    QTcpSocket *clientSocket;
};
#endif // WIDGET_H

// widget.cpp
#include "widget.h"
#include "ui_widget.h"

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

    // 给主窗口设置标题
    this->setWindowTitle("客户端");

    // 1.初始化QTcpSocket对象
    clientSocket = new QTcpSocket(this);
    // 2.绑定客户端的IP和端口号
    clientSocket->bind(QHostAddress("192.168.10.7"), 20000);

    // 关联disconnected()信号,判断客户端是否断开连接
    connect(clientSocket, &QAbstractSocket::disconnected, this, &Widget::serverUnlink);
}

Widget::~Widget()
{
    delete ui;
}

// 3.连接到服务器
void Widget::on_btn_connectServer_clicked()
{
    // 获取服务器的ip 和端口号
    QString serverIP = ui->lineEdit_inServerIP->text();
    QString serverPort = ui->lineEdit_inServerPort->text();

    // 连接服务器
    clientSocket->connectToHost(QHostAddress(serverIP), serverPort.toInt());
    // 关联 readyRead 信号,在槽函数中接收服务器发送过来的信息
    connect(clientSocket, &QIODevice::readyRead, this, &Widget::recvServerMsg);
}

// 4.发送信息到服务器
void Widget::on_btn_sendMsgServer_clicked()
{
    // 读取发送文本框的内容
    QString sendStr = ui->textEdit->toPlainText();
    // 发送给服务器
    clientSocket->write(sendStr.toUtf8());
}

// 接收服务器发送的信息
void Widget::recvServerMsg()
{
    // 在套接字文件中读取到的信息
    QByteArray recvMSg = clientSocket->readAll();
    // 在文本浏览框中显示
    ui->textBrowser->setText(recvMSg);
}

// 判断服务器断开连接
void Widget::serverUnlink()
{
    qDebug() << "服务器断开连接";
}
1.2.2.2 现象:

现象同1.2.1.2

1.3 获取对方的ip和端口号

cpp 复制代码
QHostAddress QAbstractSocket::peerAddress() const
          返回值:对方的ip
                  QString QHostAddress::toString() const
                        返回值:把QHostAddress转换字符串格式ip地址
quint16 QAbstractSocket::peerPort() const
          返回值:对方的端口号

1.3.1 示例代码(同服务器客户端代码)

cpp 复制代码
//获取对方(客户端)的ip和端口号
QString clientIP = newTcpSocket->peerAddress().toString();
quint16 clientPort = newTcpSocket->peerPort();
//拼接ip和端口号在标签上显示
QString clientIPAndPort = QString("%1@%2").arg(clientIP).arg(clientPort);
ui->label->setText(clientIPAndPort);
//在横向列表框中显示
ui->listWidget->addItem(clientIPAndPort);

1.4 多个客户端连接服务器

cpp 复制代码
//给主窗口设置标题
void QMainWindow::setWindowTitle(const QString &)
// 例如
this->setWindowTitle("服务器");
this->setWindowTitle("客户端");
//复制了客户端的代码,若不想改文件名时,运行同一个文件产生的临时文件名会相同,导致客户端程序只能运行一个
解决办法:需要去项目(QT开发环境的左侧像扳手的一样的图标),取消勾选shadow build的路径(这个路径是编译产生的临时文件所在的路径),
此时生成的临时文件就是与源文件同一个目录

1.4.1 示例代码:多个客户端与服务器之间的通信

服务器代码
cpp 复制代码
// widget.h
#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>
#include <QTcpServer>
#include <QTcpSocket>
#include <QHostAddress>
#include <QListWidgetItem>

QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE

class Widget : public QWidget
{
    Q_OBJECT

public:
    Widget(QWidget *parent = nullptr);
    ~Widget();

public slots:
    void newLink();
    void recvClientMsg();
    void clientUnlink();

private slots:
    void on_pushButton_clicked();
    void on_listWidget_itemDoubleClicked(QListWidgetItem *item);

private:
    Ui::Widget *ui;
    QTcpServer *TcpServer;
    QTcpSocket *newTcpSocket;
    QList<QTcpSocket *> ServerSocketList;
    QString clientStr;
};
#endif // WIDGET_H

// widget.cpp
#include "widget.h"
#include "ui_widget.h"

Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);
    // 给主窗口设置标题
    this->setWindowTitle("服务器");

    // 1.初始化QTcpServer对象
    TcpServer = new QTcpServer(this);

    // 2.绑定并监听
    TcpServer->listen(QHostAddress("192.168.10.7"), 10000);

    // 3.当有新的客户端连接时,会长生newConnection信号,需要主动关联newConnection信号
    connect(TcpServer, &QTcpServer::newConnection, this, &Widget::newLink);
}

Widget::~Widget()
{
    delete ui;
}

// 跟newConnection信号对应的槽函数
void Widget::newLink()
{
    qDebug()<<"有客户端连接到服务器";
    // 调用nextPendingConnection()产生新的套接字
    newTcpSocket = TcpServer->nextPendingConnection();

    //获取对方(客户端)的ip和端口号
    QString clientIP = newTcpSocket->peerAddress().toString();
    quint16 clientPort = newTcpSocket->peerPort();

    //拼接ip和端口号在标签上显示
    QString clientIPAndPort = QString("%1@%2").arg(clientIP).arg(clientPort);
    ui->label->setText(clientIPAndPort);

    //在横向列表框中显示
    ui->listWidget->addItem(clientIPAndPort);

    // 关联readyRead信号,在槽函数中接收对方发送过来的信息
    connect(newTcpSocket, &QIODevice::readyRead, this, &Widget::recvClientMsg);

    // 关联disconnected()信号,判断客户端是否断开连接
    connect(newTcpSocket, &QAbstractSocket::disconnected, this, &Widget::clientUnlink);

    // 把当前连接成功的客户端套接字存放到容器
    ServerSocketList.push_back(newTcpSocket);
}

//发送信息的槽函数
void Widget::on_pushButton_clicked()
{
    // 获取文本编辑框中输入的内容
    QString serverSendInfo = ui->textEdit->toPlainText();

    // 由于所有连接成功的客户端套接字都存放到了容器socklist里面
    // 遍历容器,找到双击的那个客户端对应的套接字
    int i;
    for (i = 0;i < ServerSocketList.size(); i++) {
        QString ip = ServerSocketList[i]->peerAddress().toString(); // ip地址
        quint16 port = ServerSocketList[i]->peerPort();         // 端口号
        QString curStr = QString("%1@%2").arg(ip).arg(port);
        if (curStr == clientStr){
            break; // 找到了双击的那个客户端
        }
    }

    // 4.把内容发送给客户端
    ServerSocketList[i]->write(serverSendInfo.toUtf8());
}

//接收客户端信息 (多个信号共用一个槽函数)
void Widget::recvClientMsg()
{
    // 获取信号的发送者
    QObject *signalSender = sender();

    // 转换成QTcpSocket指针-->即当前发送消息的客户端
    QTcpSocket *curSocket = qobject_cast<QTcpSocket *>(signalSender);

    // 在套接字中读取到的信息
    QByteArray clientMsg = curSocket->readAll();
    QString clientMsgStr(clientMsg);
    //获取对方(客户端)的ip和端口号
    QString ip = curSocket->peerAddress().toString(); // ip地址
    quint16 port = curSocket->peerPort();         // 端口号
    //拼接ip和端口号在标签上显示
    QString curStr = QString("%1@%2发来的消息:@%3").arg(ip).arg(port).arg(clientMsgStr);

    //在文本浏览框中显示出来
    ui->textBrowser->append(curStr);
}

// 客户端断开连接--->把对应的套接字删除在列表框中移除
void Widget::clientUnlink()
{
//    qDebug() << "客户端断开连接";
    // 获取断开连接的信号的发送者
    QObject *signalSender = sender();

    // 转换成QTcpSocket指针-->即当前发送消息的客户端
    QTcpSocket *curSocket = qobject_cast<QTcpSocket *>(signalSender);

    // 获取对方(客户端)的ip和端口号
    QString ip = curSocket->peerAddress().toString(); // ip地址
    quint16 port = curSocket->peerPort();         // 端口号
    // 拼接ip和端口号,这个就是要删除的客户端
    QString delStr = QString("%1@%2").arg(ip).arg(port);

    // 从容器里面把对应的套接字删除
    ServerSocketList.removeOne(curSocket);
    // 从横向列表框中删除对应的客户端信息
    // 查找列表项
    QList<QListWidgetItem*> curFindItem= ui->listWidget->findItems(delStr, Qt::MatchContains);
    //通过列表项得到索引号
    int index = ui->listWidget->row(curFindItem[0]);
    // 删除对应的客户端信息
    ui->listWidget->takeItem(index);
}
// 双击某个客户端
void Widget::on_listWidget_itemDoubleClicked(QListWidgetItem *item)
{
    //获取列表项的文本内容(某个客户端的  ip@端口)
    clientStr = item->text();
}
客户端1代码:(客户端代码基本相同,只是窗口标题和端口号不一样)
cpp 复制代码
// widget.h
#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>
#include <QTcpSocket>
#include <QHostAddress>

QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE

class Widget : public QWidget
{
    Q_OBJECT

public:
    Widget(QWidget *parent = nullptr);
    ~Widget();

private slots:
    void on_btn_connectServer_clicked();
    void on_btn_sendMsgServer_clicked();
    void recvServerMsg();
    void serverUnlink();

private:
    Ui::Widget *ui;
    QTcpSocket *clientSocket;
};
#endif // WIDGET_H

// widget.cpp
#include "widget.h"
#include "ui_widget.h"

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

    // 给主窗口设置标题
    this->setWindowTitle("客户端1");
    // 1.初始化QTcpSocket对象
    clientSocket = new QTcpSocket(this);
    // 2.绑定客户端的IP和端口号
    clientSocket->bind(QHostAddress("192.168.10.7"), 15000);

    // 关联disconnected()信号,判断客户端是否断开连接
    connect(clientSocket, &QAbstractSocket::disconnected, this, &Widget::serverUnlink);
}

Widget::~Widget()
{
    delete ui;
}

// 3.连接到服务器
void Widget::on_btn_connectServer_clicked()
{
    // 获取服务器的ip 和端口号
    QString serverIP = ui->lineEdit_inServerIP->text();
    QString serverPort = ui->lineEdit_inServerPort->text();

    // 连接服务器
    clientSocket->connectToHost(QHostAddress(serverIP), serverPort.toInt());
    // 关联 readyRead 信号,在槽函数中接收服务器发送过来的信息
    connect(clientSocket, &QIODevice::readyRead, this, &Widget::recvServerMsg);
}

// 4.发送信息到服务器
void Widget::on_btn_sendMsgServer_clicked()
{
    // 读取发送文本框的内容
    QString sendStr = ui->textEdit->toPlainText();
    // 发送给服务器
    clientSocket->write(sendStr.toUtf8());
}

// 接收服务器发送的信息
void Widget::recvServerMsg()
{
    // 在套接字文件中读取到的信息
    QByteArray recvMSg = clientSocket->readAll();
    // 在文本浏览框中显示
    ui->textBrowser->setText(recvMSg);
}

// 判断服务器断开连接
void Widget::serverUnlink()
{
    qDebug() << "服务器断开连接";
}
客户端2代码:
cpp 复制代码
// widget.h
#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>
#include <QTcpSocket>
#include <QHostAddress>

QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE

class Widget : public QWidget
{
    Q_OBJECT

public:
    Widget(QWidget *parent = nullptr);
    ~Widget();

private slots:
    void on_btn_connectServer_clicked();
    void on_btn_sendMsgServer_clicked();
    void recvServerMsg();
    void serverUnlink();

private:
    Ui::Widget *ui;
    QTcpSocket *clientSocket;
};
#endif // WIDGET_H

// widget.cpp
#include "widget.h"
#include "ui_widget.h"

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

    // 给主窗口设置标题
    this->setWindowTitle("客户端2");

    // 1.初始化QTcpSocket对象
    clientSocket = new QTcpSocket(this);
    // 2.绑定客户端的IP和端口号
    clientSocket->bind(QHostAddress("192.168.10.7"), 16000);

    // 关联disconnected()信号,判断客户端是否断开连接
    connect(clientSocket, &QAbstractSocket::disconnected, this, &Widget::serverUnlink);
}

Widget::~Widget()
{
    delete ui;
}

// 3.连接到服务器
void Widget::on_btn_connectServer_clicked()
{
    // 获取服务器的ip 和端口号
    QString serverIP = ui->lineEdit_inServerIP->text();
    QString serverPort = ui->lineEdit_inServerPort->text();

    // 连接服务器
    clientSocket->connectToHost(QHostAddress(serverIP), serverPort.toInt());
    // 关联 readyRead 信号,在槽函数中接收服务器发送过来的信息
    connect(clientSocket, &QIODevice::readyRead, this, &Widget::recvServerMsg);
}

// 4.发送信息到服务器
void Widget::on_btn_sendMsgServer_clicked()
{
    // 读取发送文本框的内容
    QString sendStr = ui->textEdit->toPlainText();
    // 发送给服务器
    clientSocket->write(sendStr.toUtf8());
}

// 接收服务器发送的信息
void Widget::recvServerMsg()
{
    // 在套接字文件中读取到的信息
    QByteArray recvMSg = clientSocket->readAll();
    // 在文本浏览框中显示
    ui->textBrowser->setText(recvMSg);
}

// 判断服务器断开连接
void Widget::serverUnlink()
{
    qDebug() << "服务器断开连接";
}
客户端3代码:
cpp 复制代码
// widget.h
#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>
#include <QTcpSocket>
#include <QHostAddress>

QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE

class Widget : public QWidget
{
    Q_OBJECT

public:
    Widget(QWidget *parent = nullptr);
    ~Widget();

private slots:
    void on_btn_connectServer_clicked();
    void on_btn_sendMsgServer_clicked();
    void recvServerMsg();
    void serverUnlink();

private:
    Ui::Widget *ui;
    QTcpSocket *clientSocket;
};
#endif // WIDGET_H

// widget.cpp
#include "widget.h"
#include "ui_widget.h"

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

    // 给主窗口设置标题
    this->setWindowTitle("客户端3");

    // 1.初始化QTcpSocket对象
    clientSocket = new QTcpSocket(this);
    // 2.绑定客户端的IP和端口号
    clientSocket->bind(QHostAddress("192.168.10.7"), 17000);

    // 关联disconnected()信号,判断客户端是否断开连接
    connect(clientSocket, &QAbstractSocket::disconnected, this, &Widget::serverUnlink);
}

Widget::~Widget()
{
    delete ui;
}

// 3.连接到服务器
void Widget::on_btn_connectServer_clicked()
{
    // 获取服务器的ip 和端口号
    QString serverIP = ui->lineEdit_inServerIP->text();
    QString serverPort = ui->lineEdit_inServerPort->text();

    // 连接服务器
    clientSocket->connectToHost(QHostAddress(serverIP), serverPort.toInt());
    // 关联 readyRead 信号,在槽函数中接收服务器发送过来的信息
    connect(clientSocket, &QIODevice::readyRead, this, &Widget::recvServerMsg);
}

// 4.发送信息到服务器
void Widget::on_btn_sendMsgServer_clicked()
{
    // 读取发送文本框的内容
    QString sendStr = ui->textEdit->toPlainText();
    // 发送给服务器
    clientSocket->write(sendStr.toUtf8());
}

// 接收服务器发送的信息
void Widget::recvServerMsg()
{
    // 在套接字文件中读取到的信息
    QByteArray recvMSg = clientSocket->readAll();
    // 在文本浏览框中显示
    ui->textBrowser->setText(recvMSg);
}

// 判断服务器断开连接
void Widget::serverUnlink()
{
    qDebug() << "服务器断开连接";
}

1.4.2 现象:

1.5 判断对方断开连接

原理:如果对方断开连接,QTcpSocket的对象会自动触发disconnected()信号,程序员自己关联这个信号,在槽函数里面写代码处理断开以后的处理

signal\] void QAbstractSocket::disconnected()

1.5.1 示例代码:

跟newConnection信号对应的槽函数中添加连接信号

cpp 复制代码
// 关联disconnected()信号,判断客户端是否断开连接
connect(newTcpSocket, &QAbstractSocket::disconnected, this, &Widget::clientUnlink);

2. udp

2.1 udp通信流程

2.1.1 发送端:

cpp 复制代码
第一步:创建QUdpSocket套接字对象,调用bind绑定ip和端口号
        QUdpSocket::QUdpSocket(QObject *parent = nullptr)
        bool QAbstractSocket::bind(const QHostAddress &address, quint16 port = 0)
           返回值: 成功 true 失败 false
             参数: address --》要绑定的ip地址
                   port --》要绑定的端口号 
第二步:收发信息
        发送信息:qint64 QUdpSocket::writeDatagram(const char *data, qint64 size, const QHostAddress &address, quint16 port) 
                          qint64 QUdpSocket::writeDatagram(const QByteArray &datagram, const QHostAddress &host, quint16 port)
                       参数: data --》要发送的内容
                              size --》打算发送多少字节的数据
                              address --》对方的ip 
                              port --》对方的端口号
        接收信息:qint64 QUdpSocket::readDatagram(char *data, qint64 maxSize, QHostAddress *address = Q_NULLPTR, quint16 *port = Q_NULLPTR)
                       参数:data --》保存接收的数据
                             maxSize --》打算接收多少字节的数据
         注意:  如果对方有发送信息过来,不能直接调用readDatagram接收
                      如果对方有发送信息过来,QUdpSocket的对象会自动触发readyRead(),程序员在自定义的槽函数里面调用readDatagram接收信息即可
第三步:关闭
        close()
2.1.1.1 示例代码:
cpp 复制代码
// widget.h
#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>
#include <QUdpSocket>
#include <QHostAddress>
#include <QDebug>

QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE

class Widget : public QWidget
{
    Q_OBJECT

public:
    Widget(QWidget *parent = nullptr);
    ~Widget();

private slots:
    void on_pushButton_clicked();

private:
    Ui::Widget *ui;
    QUdpSocket *udpSocket;
};
#endif // WIDGET_H


// widget.cpp
#include "widget.h"
#include "ui_widget.h"

Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);
    // 设置窗口标题
    this->setWindowTitle("发送端");
    // 创建udp套接字
    udpSocket = new QUdpSocket(this);
    // 绑定ip和端口号
    udpSocket->bind(QHostAddress("192.168.10.9"), 10000);
}

Widget::~Widget()
{
    delete ui;
}

// 发送消息
void Widget::on_pushButton_clicked()
{
    // 获取文本编辑框中输入的信息
    QString sendMsg = ui->textEdit->toPlainText();
    // 发送消息
    udpSocket->writeDatagram(sendMsg.toUtf8(), QHostAddress("192.168.10.9"), 20000);
    qDebug()<<"点击了发送消息";
}

2.1.2 接收端:

cpp 复制代码
第一步:创建QUdpSocket套接字对象,调用bind绑定ip和端口号
第二步:收发信息
        发送信息:qint64 QUdpSocket::writeDatagram(const char *data, qint64 size, const QHostAddress &address, quint16 port) 
                 qint64 QUdpSocket::writeDatagram(const QByteArray &datagram, const QHostAddress &host, quint16 port)
                       参数: data --》要发送的内容
                              size --》打算发送多少字节的数据
                              address --》对方的ip 
                              port --》对方的端口号
        接收信息:qint64 QUdpSocket::readDatagram(char *data, qint64 maxSize, QHostAddress *address = Q_NULLPTR, quint16 *port = Q_NULLPTR)
                       参数:data --》保存接收的数据
                             maxSize --》打算接收多少字节的数据
         注意:  如果对方有发送信息过来,不能直接调用readDatagram接收
                 如果对方有发送信息过来,QUdpSocket的对象会自动触发readyRead(),程序员在自定义的槽函数里面调用readDatagram接收信息即可
第三步:关闭
        close()
2.1.2.1 示例代码
cpp 复制代码
// widget.h
#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>
#include <QUdpSocket>
#include <QHostAddress>
#include <QDebug>

QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE

class Widget : public QWidget
{
    Q_OBJECT

public:
    Widget(QWidget *parent = nullptr);
    ~Widget();

private slots:
    void recvMsg();

private:
    Ui::Widget *ui;
    QUdpSocket *udpRecvSocket;
};
#endif // WIDGET_H


// widget.cpp
#include "widget.h"
#include "ui_widget.h"

Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);
    // 设置窗口标题
    this->setWindowTitle("接收端");
    // 创建udp套接字
    udpRecvSocket = new QUdpSocket(this);
    // 绑定ip和端口号
    udpRecvSocket->bind(QHostAddress("192.168.10.9"), 20000);
    // 主动关联readyRead()信号,在槽函数里面接收信息
    connect(udpRecvSocket, &QIODevice::readyRead, this, &Widget::recvMsg);
}

Widget::~Widget()
{
    delete ui;
}

void Widget::recvMsg()
{
    char recvBuf[100];
    udpRecvSocket->readDatagram(recvBuf,sizeof (recvBuf));
    //在文本浏览框中显示内容
    ui->textBrowser->append(recvBuf);
    qDebug()<<"接收消息槽函数";
}
2.1.2.2 现象:

2.2 bind需要注意的问题

linux中: INADDR_ANY --》获取本地主机上的任意一个ip

QT中: QHostAddress::Any --》获取本地主机上的任意一个ip

mysock.bind(QHostAddress::Any,20000,QAbstractSocket::ReuseAddressHint);

绑定任意一个ip地址,并且端口号可以复用

相关推荐
星火撩猿1 小时前
OpenCv实战笔记(1)在win11搭建opencv4.11.1 + qt5.15.2 + vs2019_x64开发环境
笔记·qt·opencv
先鱼鲨生2 小时前
【Qt】初识Qt
开发语言·qt
chao_7893 小时前
QT开发工具对比:Qt Creator、Qt Designer、Qt Design Studio
开发语言·qt
满怀10153 小时前
【ICMP协议深度解析】从网络诊断到安全实践
网络·tcp/ip·安全·icmp·网络诊断
盛满暮色 风止何安4 小时前
OSPF的路由
运维·服务器·网络·网络协议·网络安全·华为·智能路由器
feiyangqingyun4 小时前
Qt/C++源码/实时视音频通话示例/极低延迟/可外网通话/画中画/支持嵌入式板子
c++·qt·qt视音频通话
秋风&萧瑟4 小时前
【QT】QT中http协议和json数据的解析-http获取天气预报
qt·http·json
三玖诶6 小时前
理解MAC-IP映射、ARP协议与ARP欺骗及防护
网络
郑文博Coding6 小时前
WebSocket与Socket、TCP、HTTP的关系及区别
websocket·tcp/ip·http