当前位置: 首页 > news >正文

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

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

  • 1.tcp
    • 1.1 tcp通信
      • 1.1.1 相比linux中tcp通信:
      • 1.1.2 QT中的tcp通信:
    • 1.2 tcp通信流程
      • 1.2.1 服务器流程:
        • 1.2.1.1 示例代码
        • 1.2.1.2 现象
      • 1.2.2 客户端流程:
        • 1.2.2.1 示例代码
        • 1.2.2.2 现象:
    • 1.3 获取对方的ip和端口号
      • 1.3.1 示例代码(同服务器客户端代码)
    • 1.4 多个客户端连接服务器
      • 1.4.1 示例代码:多个客户端与服务器之间的通信
        • 服务器代码
        • 客户端1代码:(客户端代码基本相同,只是窗口标题和端口号不一样)
        • 客户端2代码:
        • 客户端3代码:
      • 1.4.2 现象:
    • 1.5 判断对方断开连接
      • 1.5.1 示例代码:
  • 2. udp
    • 2.1 udp通信流程
      • 2.1.1 发送端:
        • 2.1.1.1 示例代码:
      • 2.1.2 接收端:
        • 2.1.2.1 示例代码
        • 2.1.2.2 现象:
    • 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 服务器流程:

第一步: 创建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 示例代码
// 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_NAMESPACEclass Widget : public QWidget
{Q_OBJECTpublic: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 客户端流程:

第一步:创建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 示例代码
// widget.h
#ifndef WIDGET_H
#define WIDGET_H#include <QWidget>
#include <QTcpSocket>
#include <QHostAddress>QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACEclass Widget : public QWidget
{Q_OBJECTpublic: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和端口号

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

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

//获取对方(客户端)的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 多个客户端连接服务器

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

在这里插入图片描述

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

服务器代码
// 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_NAMESPACEclass Widget : public QWidget
{Q_OBJECTpublic: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代码:(客户端代码基本相同,只是窗口标题和端口号不一样)
// widget.h
#ifndef WIDGET_H
#define WIDGET_H#include <QWidget>
#include <QTcpSocket>
#include <QHostAddress>QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACEclass Widget : public QWidget
{Q_OBJECTpublic: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代码:
// widget.h
#ifndef WIDGET_H
#define WIDGET_H#include <QWidget>
#include <QTcpSocket>
#include <QHostAddress>QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACEclass Widget : public QWidget
{Q_OBJECTpublic: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代码:
// widget.h
#ifndef WIDGET_H
#define WIDGET_H#include <QWidget>
#include <QTcpSocket>
#include <QHostAddress>QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACEclass Widget : public QWidget
{Q_OBJECTpublic: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信号对应的槽函数中添加连接信号

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

2. udp

2.1 udp通信流程

2.1.1 发送端:

第一步:创建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 示例代码:
// 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_NAMESPACEclass Widget : public QWidget
{Q_OBJECTpublic: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 接收端:

第一步:创建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 示例代码
// 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_NAMESPACEclass Widget : public QWidget
{Q_OBJECTpublic: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地址,并且端口号可以复用

相关文章:

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

QT中的网络编程&#xff08;TCP 和 UDP通信&#xff09; 1.tcp1.1 tcp通信1.1.1 相比linux中tcp通信:1.1.2 QT中的tcp通信: 1.2 tcp通信流程1.2.1 服务器流程&#xff1a;1.2.1.1 示例代码1.2.1.2 现象 1.2.2 客户端流程&#xff1a;1.2.2.1 示例代码1.2.2.2 现象&#xff1a; …...

个性化推荐:大数据引领电子商务精准营销新时代

个性化推荐:大数据引领电子商务精准营销新时代 引言 在电子商务的时代,个性化推荐系统已经成为提升用户体验、增强平台竞争力的重要技术。随着大数据技术的迅猛发展,传统的推荐方法已经无法满足用户日益增长的需求。为了精准地把握用户兴趣和消费倾向,商家们依赖大数据分析…...

【前端】【总复习】HTML

一、HTML&#xff08;结构&#xff09; HTML 是网页的骨架&#xff0c;主要负责网页的结构与语义表达&#xff0c;为 CSS 和 JavaScript 提供承载基础。 1.1 HTML 基本结构与语义化标签 1.1.1 HTML 基本结构 <!DOCTYPE html> <html lang"en"> <hea…...

Android 输入控件事件使用示例

一 前端 <EditTextandroid:id="@+id/editTextText2"android:layout_width="match_parent"android:layout_height="wrap_content"android:ems="10"android:inputType="text"android:text="Name" />二 后台代…...

JVM happens-before 原则有哪些?

理解Java Memory Model (JMM) 中的 happens-before 原则对于编写并发程序有很大帮助。 Happens-before 关系是 JMM 用来描述两个操作之间的内存可见性以及执行顺序的抽象概念。如果一个操作 A happens-before 另一个操作 B (记作 A hb B)&#xff0c;那么 JMM 向你保证&#x…...

Python实例题:Python获取NBA数据

目录 Python实例题 题目 方式一&#xff1a;使用网页爬虫获取数据 代码解释 get_nba_schedule 函数&#xff1a; 主程序&#xff1a; 方式二&#xff1a;使用专业 API 获取数据 代码解释 运行思路 方式一 方式二 注意事项 以下是完整的 doubaocanvas 代码块&#…...

【中间件】brpc_基础_remote_task_queue

文章目录 remote task queue1 简介2 核心功能2.1 任务提交与分发2.2 无锁或低锁设计2.3 与 bthread 深度集成2.4 流量控制与背压 3 关键实现机制3.1 数据结构3.2 任务提交接口3.3 任务窃取&#xff08;Work Stealing&#xff09;3.4 同步与唤醒 4 性能优化5 典型应用场景6 代码…...

React-router v7 第七章(导航)

导航 在React-router V7中&#xff0c;大致有四种导航方式&#xff1a; 使用Link组件 link使用NavLink组件 navlink使用编程式导航useNavigate usenavigate使用redirect重定向 redirect Link Link组件是一个用于导航到其他页面的组件&#xff0c;他会被渲染成一个<a>…...

Terraform 中的 external 数据块是什么?如何使用?

在 Terraform 中&#xff0c;external 数据块&#xff08;Data Block&#xff09; 是一种特殊的数据源&#xff0c;允许你通过调用外部程序或脚本获取动态数据&#xff0c;并将结果集成到 Terraform 配置中。它适用于需要从 Terraform 外部的系统或工具获取信息的场景。 一、e…...

打印Excel表格时单元格文字内容被下一行遮盖的解决方法

本文介绍在打印Excel表格文件时&#xff0c;单元格最后一行的文字内容被下一行单元格遮挡的解决方法。 最近&#xff0c;需要打印一个Excel表格文件。其中&#xff0c;已知对于表格中的单元格&#xff0c;都设置了自动换行&#xff0c;如下图所示。 并且&#xff0c;也都设置了…...

【Linux】命令行参数与环境变量

&#x1f31f;&#x1f31f;作者主页&#xff1a;ephemerals__ &#x1f31f;&#x1f31f;所属专栏&#xff1a;Linux 目录 前言 一、命令行参数 1. 什么是命令行参数 2. 命令行参数的作用 二、环境变量 1. 基本概念 2. 常见的环境变量 3. 环境变量相关操作 定义…...

Dify 完全指南(一):从零搭建开源大模型应用平台(Ollama/VLLM本地模型接入实战)》

文章目录 1. 相关资源2. 核心特性3. 安装与使用&#xff08;Docker Compose 部署&#xff09;3.1 部署Dify3.2 更新Dify3.3 重启Dify3.4 访问Dify 4. 接入本地模型4.1 接入 Ollama 本地模型4.1.1 步骤4.1.2 常见问题 4.2 接入 Vllm 本地模型 5. 进阶应用场景6. 总结 1. 相关资源…...

民法学学习笔记(个人向) Part.3

民法学学习笔记&#xff08;个人向&#xff09; Part.3 8. 诉讼时效&#x1f338; 概念&#xff1a; 是指权利主体在法定期间内不行使权利&#xff0c;则债务人享有抗辩权&#xff0c;可以导致权利人无法胜诉的法律制度&#xff08;有权你不用&#xff0c;别人就有话说了&#…...

C# 方法(返回值、返回语句和void方法)

本章内容: 方法的结构 方法体内部的代码执行 局部变量 局部常量 控制流 方法调用 返回值 返回语句和void方法 局部函数 参数 值参数 引用参数 引用类型作为值参数和引用参数 输出参数 参数数组 参数类型总结 方法重载 命名参数 可选参数 栈帧 递归 返回值 方法可以向调用代码返…...

打电话玩手机检测数据集VOC+YOLO格式8061张1类别

数据集格式&#xff1a;Pascal VOC格式YOLO格式(不包含分割路径的txt文件&#xff0c;仅仅包含jpg图片以及对应的VOC格式xml文件和yolo格式txt文件) 图片数量(jpg文件个数)&#xff1a;8061 标注数量(xml文件个数)&#xff1a;8061 标注数量(txt文件个数)&#xff1a;8061 …...

详解如何压测RocketMQ

目录 1.如何设计压测 2.压测工具 3.硬件配置 4.写代码压测 5.自带压测脚本 1.如何设计压测 二八定律法则&#xff1a;按业务峰值的 120% 设计压测目标&#xff08;若线上峰值1000TPS&#xff0c;压测目标至少1200TPS&#xff09; 关注三个指标 吞吐量&#xff08;TPS&…...

实验三 触发器及基本时序电路

1.触发器的分类&#xff1f;各自的特点是什么&#xff1f; 1 、 D 触发器 特点&#xff1a;只有一个数据输入端 D &#xff0c;在时钟脉冲的触发沿&#xff0c;输出 Q 的状态跟随输入端 D 的 状态变化&#xff0c;即 &#xff0c;功能直观&#xff0c;利于理解和感受…...

双列集合——map集合和三种遍历方式

双列集合的特点 键和值一一对应&#xff0c;每个键只能对应自己的值 一个键和值整体称为键值对或键值对对象&#xff0c;java中叫做entry对象。 map常见的api map接口中定义了双列集合所有的共性方法&#xff0c;下面三个实现类就没有什么额外新的方法要学习了。 map接口…...

WebRTC 服务器之Janus视频会议插件信令交互

1.基础知识回顾 WebRTC 服务器之Janus概述和环境搭建-CSDN博客 WebRTC 服务器之Janus架构分析-CSDN博客 2.插件使用流程 我们要使⽤janus的功能时&#xff0c;通常要执⾏以下操作&#xff1a; 1. 在你的⽹⻚引入 Janus.js 库&#xff0c;即是包含janus.js&#xff1b; <…...

LabVIEW温控系统热敏电阻滞后问题

在 LabVIEW 构建的温控系统中&#xff0c;热敏电阻因热时间常数大&#xff08;2 秒左右&#xff09;产生的滞后效应&#xff0c;致使控温出现超调与波动。在不更换传感器的前提下&#xff0c;可从算法优化、硬件调整和系统设计等维度着手解决。 ​ 一、算法优化​ 1. 改进 PI…...

【Unity】使用XLua进行热修复

准备工作&#xff1a; &#xff08;1&#xff09;将XLua的Tool拖入Asset &#xff08;2&#xff09;配置热修复 &#xff08;3&#xff09;运行Genrate Code &#xff08;4&#xff09;运行Hotfix Inject In Editor 编写脚本&#xff08;注意类上带有[Hotfix]&#xff09; [Hot…...

GateWay使用

首先创建一个网关服务&#xff0c;添加对应的依赖 <dependencies><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-gateway</artifactId></dependency><dependency><groupId&…...

如何使用责任链模式优雅实现功能(滴滴司机、家政服务、请假审批等)

在企业级开发中&#xff0c;我们经常会遇到一系列有先后顺序、逐步处理的逻辑链路&#xff0c;例如请假审批、报销审批、日志处理、事件处理、滴滴司机接单流程等。这些场景非常适合使用 责任链模式&#xff08;Chain of Responsibility Pattern&#xff09; 来优雅地实现。 本…...

opencv的contours

1.哪里来的contours&#xff1a; 我们常常用到这一句&#xff1a; contours, hierarchy cv2.findContours(img, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) 输出的contours的是一个tuple类型的&#xff1a; 1.它的len&#xff08;&#xff09;就是contour的个数 2.每一…...

使用 OpenCV 和 Dlib实现轮廓绘制

文章目录 引言1.准备工作2.代码解析2.1 导入必要的库2.2 定义绘制直线函数2.3 定义绘制凸包函数2.4 加载图像和模型2.5 关键点检测与绘制2.6 显示结果 3.68个关键点索引说明4.应用场景5.优化建议6. 结语 引言 人脸关键点检测是计算机视觉中的重要任务&#xff0c;广泛应用于人…...

学习黑客Linux 命令

在操作下面的闯关题之前&#xff0c;给下学习资料 一图速览&#xff1a;20 条命令及练习手册 #命令 & 常用参数关键作用典型练习1ls -alh列文件&#xff08;含隐藏 & 人类可读大小&#xff09;(数字海洋)在 $HOME 统计目录数2cd / pwd切换、显示路径cd /tmp &&a…...

探秘 RocketMQ 的 DLedgerServer:MemberState 的技术解析与深度剖析

在 RocketMQ 构建高可靠、强一致性消息系统的架构中&#xff0c;DLedgerServer 扮演着举足轻重的角色&#xff0c;而 MemberState 作为 DLedgerServer 内部用于描述节点状态的核心类&#xff0c;更是整个分布式日志模块稳定运行的关键。深入理解 MemberState 的设计理念、功能特…...

【计算机网络】HTTP中GET和POST的区别是什么?

从以下几个方面去说明&#xff1a; 1.定义 2.参数传递方式 3.安全性 4.幂等性 1.定义&#xff1a; GET&#xff1a; 获取资源&#xff0c;通常请求数据而不改变服务器的状态。POST&#xff1a; 提交数据到服务器&#xff0c;通常会改变服务器的状态或副作用(如创建或更新资源…...

C++负载均衡远程调用学习之Agent代理模块基础构建

目录 1.课程复习 2.Lars-lbAgentV0.1-udpserver启动 3.Lars-lbAgentV0.1-dns-reporter-client-thread启动 4.Lars-lbAgentV0.1-dns-client实现 5.Lars-lbAgentV0.1-dns-client编译错误修正 6.Lars-lbAgentV0.1-reporter_client实现 1.课程复习 ### 11.2 完成Lars Reactor…...

游戏开发的TypeScript(4)TypeScript 的一些内置函数

在 TypeScript 中&#xff0c;内置函数分为两类&#xff1a;JavaScript 原生函数&#xff08;TypeScript 继承&#xff09;和TypeScript 特有函数&#xff08;类型系统相关&#xff09;。以下是详细分类介绍&#xff1a; 一、JavaScript 原生函数 TypeScript 继承了所有 Java…...

软考 系统架构设计师系列知识点之杂项集萃(52)

接前一篇文章&#xff1a;软考 系统架构设计师系列知识点之杂项集萃&#xff08;51&#xff09; 第82题 以下关于系统性能的叙述中&#xff0c;不正确的是&#xff08;&#xff09;。 A. 常见的Web服务器性能评估方法有基准测试、压力测试和可靠性测试 B. 评价Web服务器的主…...

大连理工大学选修课——图形学:第五章 二维变换及二维观察

第五章 二维变换及二维观察 二维变换 基本几何变换 图形的几何变换是指对图形的几何信息经过平移、比例、旋转等变换后产生新的图形。 基本几何变换都是相对于坐标原点和坐标轴进行的几何变换。 平移变换 推导&#xff1a; x ′ x T x y ′ y T y xxT_x\\ yyT_y x′xT…...

观察者模式(Observer Pattern)详解

文章目录 1. 什么是观察者模式?2. 为什么需要观察者模式?3. 观察者模式的核心概念4. 观察者模式的结构5. 观察者模式的基本实现简单的气象站示例6. 观察者模式的进阶实现推模型 vs 拉模型6.1 推模型(Push Model)6.2 拉模型(Pull Model)7. 观察者模式的复杂实现7.1 在线商…...

复刻低成本机械臂 SO-ARM100 标定篇

视频讲解&#xff1a; 复刻低成本机械臂 SO-ARM100 标定篇 组装完机械臂后&#xff0c;要进行初始标定&#xff0c;参考github的markdown lerobot/examples/10_use_so100.md at main huggingface/lerobot 只有从臂&#xff0c;所以arms里面只填follower即可 python lerobot…...

idea创建springboot工程-指定阿里云地址创建工程报错

idea创建springboot工程-指定阿里云地址创建工程报错 提示&#xff1a;帮帮志会陆续更新非常多的IT技术知识&#xff0c;希望分享的内容对您有用。本章分享的是springboot的使用。前后每一小节的内容是存在的有&#xff1a;学习and理解的关联性。【帮帮志系列文章】&#xff1…...

OpenStack HA高可用集群Train版-0集群环境准备

OpenStack HA高可用集群Train版-0集群环境准备 目录 主机配置1.主机名2.网卡配置网卡UUID配置主机名解析配置免密登录防火墙相关配置时间同步配置 二、基础软件安装数据库构建数据库集群设置心跳检测clustercheck准备脚本创建心跳检测用户,(任意控制节点)检测配置文件每个控制节…...

Python3与Dubbo3.1通讯解决方案(dubbo-python)

【文章非VIP可读&#xff0c;如果发现阅读限制为系统自动修改阅读权限&#xff0c;请留言我改回】 概述 最近AI项目需要java与python通讯&#xff0c;两边都是比较新的版本。因此需要双方进行通讯&#xff0c;在这里记录一下所采用的方案和关键点。 JAVA调用Python python通…...

深入探索 Java 区块链技术:从核心原理到企业级实践

一、Java 与区块链的天然契合 1.1 区块链技术的核心特征 区块链作为一种分布式账本技术&#xff0c;其核心特征包括&#xff1a; 去中心化&#xff1a;通过 P2P 网络实现节点自治&#xff0c;消除对中央机构的依赖。不可篡改性&#xff1a;利用哈希链和共识机制确保数据一旦…...

NV214NV217美光闪存固态NV218NV225

NV214NV217美光闪存固态NV218NV225 在当今数据驱动的时代&#xff0c;固态硬盘&#xff08;SSD&#xff09;的性能直接决定了计算系统的效率上限。美光科技作为全球存储解决方案的领军者&#xff0c;其NV系列产品凭借尖端技术持续刷新行业标准。本文将围绕NV214、NV217、NV218、…...

第三方组件库:element-uiiviewVant

第三方组件库&#xff1a;element-ui 使用方法&#xff1a; 1.引入样式 <!-- 引入element-ui样式 --><link rel"stylesheet" type"text/css" href"http://unpkg.com/view-design/dist/styles/iview.css">2.引入vue <!-- 引入Vue …...

Qt实现 hello world + 内存泄漏(5)

文章目录 实现hello world的两种方式通过图形化的方式通过纯代码的方式1. 新老头文件的说明2.堆或栈上创建对象的选择3.QString的说明 内存泄漏问题 实现hello world的两种方式 通过图形化的方式 通过图形化的方式&#xff0c;在界面上创建出一个控件&#xff0c;显示出hello …...

13:图像处理—畸变矫正详解

1.制作标定板和描述文件 &#xff08;用PS软件打印&#xff09; * 0.00375 mark 点间距 &#xff0c; 不是 点的直径//倒数第二个就是描述文件 gen_caltab(7,7,0.00375,0.5,caltab_30mm.descr,30-30.ps) * 1 比 1 打印 。Photoshop 格式 2.把标定板调正 调正的目的是为了…...

Prompt compress 技术探究-LLMLingua

Prompt summary&#xff1a;是通过精心设计的提示词&#xff08;prompt&#xff09;引导大型语言模型&#xff08;如 GPT-4&#xff09;生成特定风格或结构的摘要。其目标不仅是压缩信息&#xff0c;还包括满足特定的格式要求、风格偏好或任务需求&#xff0c;所以和一般的文本…...

Python|Pyppeteer实现自动登录小红书(32)

前言 本文是该专栏的第32篇,结合优质项目案例持续分享Pyppeteer的干货知识,记得关注。 本文中,笔者以小红书为例,基于Pyppeteer实现自动登录“小红书”。 需要注意的是,对Pyppeteer不太熟悉的同学,可往前翻阅本专栏前面介绍的Pyppeteer知识点,本专栏将带你了解并熟练使…...

Milvus(13):自定义分析器、过滤器

1 自定义分析器 1.1 标准标记符 Milvus 中的standard 令牌分割器根据空格和标点符号分割文本&#xff0c;适用于大多数语言。要配置使用standard 令牌转换器的分析器&#xff0c;请在analyzer_params 中将tokenizer 设置为standard 。 analyzer_params {"tokenizer&quo…...

调试Cortex-M85 MCU启动汇编和链接命令文件 - 解题一则

调试Cortex-M85 MCU启动汇编和链接命令文件 - 解题一则 苏勇 Andrew, 2025-05 最近在Keil中调试一款新的Cortex-M85内核MCU的SDK代码时&#xff0c;从原有其它芯片的工程中引入了汇编语言编写的启动代码和配套的sct文件&#xff0c;结果总是报错&#xff0c;清理到最后&#…...

SpringMVC——第五章:视图View

一、SpringMVC中视图的实现原理 1.Spring MVC视图支持可配置 在Spring MVC中&#xff0c;视图View是支持定制的&#xff0c;例如我们之前在 springmvc.xml 文件中进行了如下的配置&#xff1a; <!--视图解析器--> <bean id"thymeleafViewResolver" class…...

【PostgreSQL数据分析实战:从数据清洗到可视化全流程】3.4 数据重复与去重(IDENTITY COLUMN/UNIQUE约束)

&#x1f449; 点击关注不迷路 &#x1f449; 点击关注不迷路 &#x1f449; 点击关注不迷路 文章大纲 PostgreSQL数据分析实战&#xff1a;数据质量分析之数据重复与去重&#xff08;IDENTITY COLUMN/UNIQUE约束&#xff09;3.4 数据重复与去重3.4.1 数据重复的影响与识别3.4.…...

数据分析之药物-基因-代谢物

记录一下最近的数据分析过程&#xff1a; 假如我有一个Dataframe&#xff0c;有两列[Drug, Gene]&#xff0c;我想构造一个矩阵&#xff0c;行名为Drug&#xff0c;列名为Gene&#xff0c;值为0或者1&#xff0c;其中0表示药物的靶点是该基因&#xff0c;0表示不是靶点。 &am…...

【PostgreSQL数据分析实战:从数据清洗到可视化全流程】3.2 缺失值检测与处理(NULL值填充/删除策略)

&#x1f449; 点击关注不迷路 &#x1f449; 点击关注不迷路 &#x1f449; 点击关注不迷路 文章大纲 缺失值检测与处理全攻略&#xff1a;NULL值填充与删除策略实战3.2 缺失值检测与处理3.2.1 缺失值类型与业务影响3.2.1.1 缺失值的三种形态3.2.1.2 业务影响分级 3.2.2 缺失值…...