【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中的网络编程(TCP 和 UDP通信) 1.tcp1.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 现象: …...
个性化推荐:大数据引领电子商务精准营销新时代
个性化推荐:大数据引领电子商务精准营销新时代 引言 在电子商务的时代,个性化推荐系统已经成为提升用户体验、增强平台竞争力的重要技术。随着大数据技术的迅猛发展,传统的推荐方法已经无法满足用户日益增长的需求。为了精准地把握用户兴趣和消费倾向,商家们依赖大数据分析…...
【前端】【总复习】HTML
一、HTML(结构) HTML 是网页的骨架,主要负责网页的结构与语义表达,为 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),那么 JMM 向你保证&#x…...
Python实例题:Python获取NBA数据
目录 Python实例题 题目 方式一:使用网页爬虫获取数据 代码解释 get_nba_schedule 函数: 主程序: 方式二:使用专业 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 任务窃取(Work Stealing)3.4 同步与唤醒 4 性能优化5 典型应用场景6 代码…...
React-router v7 第七章(导航)
导航 在React-router V7中,大致有四种导航方式: 使用Link组件 link使用NavLink组件 navlink使用编程式导航useNavigate usenavigate使用redirect重定向 redirect Link Link组件是一个用于导航到其他页面的组件,他会被渲染成一个<a>…...
Terraform 中的 external 数据块是什么?如何使用?
在 Terraform 中,external 数据块(Data Block) 是一种特殊的数据源,允许你通过调用外部程序或脚本获取动态数据,并将结果集成到 Terraform 配置中。它适用于需要从 Terraform 外部的系统或工具获取信息的场景。 一、e…...
打印Excel表格时单元格文字内容被下一行遮盖的解决方法
本文介绍在打印Excel表格文件时,单元格最后一行的文字内容被下一行单元格遮挡的解决方法。 最近,需要打印一个Excel表格文件。其中,已知对于表格中的单元格,都设置了自动换行,如下图所示。 并且,也都设置了…...
【Linux】命令行参数与环境变量
🌟🌟作者主页:ephemerals__ 🌟🌟所属专栏:Linux 目录 前言 一、命令行参数 1. 什么是命令行参数 2. 命令行参数的作用 二、环境变量 1. 基本概念 2. 常见的环境变量 3. 环境变量相关操作 定义…...
Dify 完全指南(一):从零搭建开源大模型应用平台(Ollama/VLLM本地模型接入实战)》
文章目录 1. 相关资源2. 核心特性3. 安装与使用(Docker Compose 部署)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
民法学学习笔记(个人向) Part.3 8. 诉讼时效🌸 概念: 是指权利主体在法定期间内不行使权利,则债务人享有抗辩权,可以导致权利人无法胜诉的法律制度(有权你不用,别人就有话说了&#…...
C# 方法(返回值、返回语句和void方法)
本章内容: 方法的结构 方法体内部的代码执行 局部变量 局部常量 控制流 方法调用 返回值 返回语句和void方法 局部函数 参数 值参数 引用参数 引用类型作为值参数和引用参数 输出参数 参数数组 参数类型总结 方法重载 命名参数 可选参数 栈帧 递归 返回值 方法可以向调用代码返…...
打电话玩手机检测数据集VOC+YOLO格式8061张1类别
数据集格式:Pascal VOC格式YOLO格式(不包含分割路径的txt文件,仅仅包含jpg图片以及对应的VOC格式xml文件和yolo格式txt文件) 图片数量(jpg文件个数):8061 标注数量(xml文件个数):8061 标注数量(txt文件个数):8061 …...
详解如何压测RocketMQ
目录 1.如何设计压测 2.压测工具 3.硬件配置 4.写代码压测 5.自带压测脚本 1.如何设计压测 二八定律法则:按业务峰值的 120% 设计压测目标(若线上峰值1000TPS,压测目标至少1200TPS) 关注三个指标 吞吐量(TPS&…...
实验三 触发器及基本时序电路
1.触发器的分类?各自的特点是什么? 1 、 D 触发器 特点:只有一个数据输入端 D ,在时钟脉冲的触发沿,输出 Q 的状态跟随输入端 D 的 状态变化,即 ,功能直观,利于理解和感受…...
双列集合——map集合和三种遍历方式
双列集合的特点 键和值一一对应,每个键只能对应自己的值 一个键和值整体称为键值对或键值对对象,java中叫做entry对象。 map常见的api map接口中定义了双列集合所有的共性方法,下面三个实现类就没有什么额外新的方法要学习了。 map接口…...
WebRTC 服务器之Janus视频会议插件信令交互
1.基础知识回顾 WebRTC 服务器之Janus概述和环境搭建-CSDN博客 WebRTC 服务器之Janus架构分析-CSDN博客 2.插件使用流程 我们要使⽤janus的功能时,通常要执⾏以下操作: 1. 在你的⽹⻚引入 Janus.js 库,即是包含janus.js; <…...
LabVIEW温控系统热敏电阻滞后问题
在 LabVIEW 构建的温控系统中,热敏电阻因热时间常数大(2 秒左右)产生的滞后效应,致使控温出现超调与波动。在不更换传感器的前提下,可从算法优化、硬件调整和系统设计等维度着手解决。 一、算法优化 1. 改进 PI…...
【Unity】使用XLua进行热修复
准备工作: (1)将XLua的Tool拖入Asset (2)配置热修复 (3)运行Genrate Code (4)运行Hotfix Inject In Editor 编写脚本(注意类上带有[Hotfix]) [Hot…...
GateWay使用
首先创建一个网关服务,添加对应的依赖 <dependencies><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-gateway</artifactId></dependency><dependency><groupId&…...
如何使用责任链模式优雅实现功能(滴滴司机、家政服务、请假审批等)
在企业级开发中,我们经常会遇到一系列有先后顺序、逐步处理的逻辑链路,例如请假审批、报销审批、日志处理、事件处理、滴滴司机接单流程等。这些场景非常适合使用 责任链模式(Chain of Responsibility Pattern) 来优雅地实现。 本…...
opencv的contours
1.哪里来的contours: 我们常常用到这一句: contours, hierarchy cv2.findContours(img, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) 输出的contours的是一个tuple类型的: 1.它的len()就是contour的个数 2.每一…...
使用 OpenCV 和 Dlib实现轮廓绘制
文章目录 引言1.准备工作2.代码解析2.1 导入必要的库2.2 定义绘制直线函数2.3 定义绘制凸包函数2.4 加载图像和模型2.5 关键点检测与绘制2.6 显示结果 3.68个关键点索引说明4.应用场景5.优化建议6. 结语 引言 人脸关键点检测是计算机视觉中的重要任务,广泛应用于人…...
学习黑客Linux 命令
在操作下面的闯关题之前,给下学习资料 一图速览:20 条命令及练习手册 #命令 & 常用参数关键作用典型练习1ls -alh列文件(含隐藏 & 人类可读大小)(数字海洋)在 $HOME 统计目录数2cd / pwd切换、显示路径cd /tmp &&a…...
探秘 RocketMQ 的 DLedgerServer:MemberState 的技术解析与深度剖析
在 RocketMQ 构建高可靠、强一致性消息系统的架构中,DLedgerServer 扮演着举足轻重的角色,而 MemberState 作为 DLedgerServer 内部用于描述节点状态的核心类,更是整个分布式日志模块稳定运行的关键。深入理解 MemberState 的设计理念、功能特…...
【计算机网络】HTTP中GET和POST的区别是什么?
从以下几个方面去说明: 1.定义 2.参数传递方式 3.安全性 4.幂等性 1.定义: GET: 获取资源,通常请求数据而不改变服务器的状态。POST: 提交数据到服务器,通常会改变服务器的状态或副作用(如创建或更新资源…...
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 中,内置函数分为两类:JavaScript 原生函数(TypeScript 继承)和TypeScript 特有函数(类型系统相关)。以下是详细分类介绍: 一、JavaScript 原生函数 TypeScript 继承了所有 Java…...
软考 系统架构设计师系列知识点之杂项集萃(52)
接前一篇文章:软考 系统架构设计师系列知识点之杂项集萃(51) 第82题 以下关于系统性能的叙述中,不正确的是()。 A. 常见的Web服务器性能评估方法有基准测试、压力测试和可靠性测试 B. 评价Web服务器的主…...
大连理工大学选修课——图形学:第五章 二维变换及二维观察
第五章 二维变换及二维观察 二维变换 基本几何变换 图形的几何变换是指对图形的几何信息经过平移、比例、旋转等变换后产生新的图形。 基本几何变换都是相对于坐标原点和坐标轴进行的几何变换。 平移变换 推导: 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 标定篇
视频讲解: 复刻低成本机械臂 SO-ARM100 标定篇 组装完机械臂后,要进行初始标定,参考github的markdown lerobot/examples/10_use_so100.md at main huggingface/lerobot 只有从臂,所以arms里面只填follower即可 python lerobot…...
idea创建springboot工程-指定阿里云地址创建工程报错
idea创建springboot工程-指定阿里云地址创建工程报错 提示:帮帮志会陆续更新非常多的IT技术知识,希望分享的内容对您有用。本章分享的是springboot的使用。前后每一小节的内容是存在的有:学习and理解的关联性。【帮帮志系列文章】࿱…...
OpenStack HA高可用集群Train版-0集群环境准备
OpenStack HA高可用集群Train版-0集群环境准备 目录 主机配置1.主机名2.网卡配置网卡UUID配置主机名解析配置免密登录防火墙相关配置时间同步配置 二、基础软件安装数据库构建数据库集群设置心跳检测clustercheck准备脚本创建心跳检测用户,(任意控制节点)检测配置文件每个控制节…...
Python3与Dubbo3.1通讯解决方案(dubbo-python)
【文章非VIP可读,如果发现阅读限制为系统自动修改阅读权限,请留言我改回】 概述 最近AI项目需要java与python通讯,两边都是比较新的版本。因此需要双方进行通讯,在这里记录一下所采用的方案和关键点。 JAVA调用Python python通…...
深入探索 Java 区块链技术:从核心原理到企业级实践
一、Java 与区块链的天然契合 1.1 区块链技术的核心特征 区块链作为一种分布式账本技术,其核心特征包括: 去中心化:通过 P2P 网络实现节点自治,消除对中央机构的依赖。不可篡改性:利用哈希链和共识机制确保数据一旦…...
NV214NV217美光闪存固态NV218NV225
NV214NV217美光闪存固态NV218NV225 在当今数据驱动的时代,固态硬盘(SSD)的性能直接决定了计算系统的效率上限。美光科技作为全球存储解决方案的领军者,其NV系列产品凭借尖端技术持续刷新行业标准。本文将围绕NV214、NV217、NV218、…...
第三方组件库:element-uiiviewVant
第三方组件库:element-ui 使用方法: 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的两种方式 通过图形化的方式 通过图形化的方式,在界面上创建出一个控件,显示出hello …...
13:图像处理—畸变矫正详解
1.制作标定板和描述文件 (用PS软件打印) * 0.00375 mark 点间距 , 不是 点的直径//倒数第二个就是描述文件 gen_caltab(7,7,0.00375,0.5,caltab_30mm.descr,30-30.ps) * 1 比 1 打印 。Photoshop 格式 2.把标定板调正 调正的目的是为了…...
Prompt compress 技术探究-LLMLingua
Prompt summary:是通过精心设计的提示词(prompt)引导大型语言模型(如 GPT-4)生成特定风格或结构的摘要。其目标不仅是压缩信息,还包括满足特定的格式要求、风格偏好或任务需求,所以和一般的文本…...
Python|Pyppeteer实现自动登录小红书(32)
前言 本文是该专栏的第32篇,结合优质项目案例持续分享Pyppeteer的干货知识,记得关注。 本文中,笔者以小红书为例,基于Pyppeteer实现自动登录“小红书”。 需要注意的是,对Pyppeteer不太熟悉的同学,可往前翻阅本专栏前面介绍的Pyppeteer知识点,本专栏将带你了解并熟练使…...
Milvus(13):自定义分析器、过滤器
1 自定义分析器 1.1 标准标记符 Milvus 中的standard 令牌分割器根据空格和标点符号分割文本,适用于大多数语言。要配置使用standard 令牌转换器的分析器,请在analyzer_params 中将tokenizer 设置为standard 。 analyzer_params {"tokenizer&quo…...
调试Cortex-M85 MCU启动汇编和链接命令文件 - 解题一则
调试Cortex-M85 MCU启动汇编和链接命令文件 - 解题一则 苏勇 Andrew, 2025-05 最近在Keil中调试一款新的Cortex-M85内核MCU的SDK代码时,从原有其它芯片的工程中引入了汇编语言编写的启动代码和配套的sct文件,结果总是报错,清理到最后&#…...
SpringMVC——第五章:视图View
一、SpringMVC中视图的实现原理 1.Spring MVC视图支持可配置 在Spring MVC中,视图View是支持定制的,例如我们之前在 springmvc.xml 文件中进行了如下的配置: <!--视图解析器--> <bean id"thymeleafViewResolver" class…...
【PostgreSQL数据分析实战:从数据清洗到可视化全流程】3.4 数据重复与去重(IDENTITY COLUMN/UNIQUE约束)
👉 点击关注不迷路 👉 点击关注不迷路 👉 点击关注不迷路 文章大纲 PostgreSQL数据分析实战:数据质量分析之数据重复与去重(IDENTITY COLUMN/UNIQUE约束)3.4 数据重复与去重3.4.1 数据重复的影响与识别3.4.…...
数据分析之药物-基因-代谢物
记录一下最近的数据分析过程: 假如我有一个Dataframe,有两列[Drug, Gene],我想构造一个矩阵,行名为Drug,列名为Gene,值为0或者1,其中0表示药物的靶点是该基因,0表示不是靶点。 &am…...
【PostgreSQL数据分析实战:从数据清洗到可视化全流程】3.2 缺失值检测与处理(NULL值填充/删除策略)
👉 点击关注不迷路 👉 点击关注不迷路 👉 点击关注不迷路 文章大纲 缺失值检测与处理全攻略:NULL值填充与删除策略实战3.2 缺失值检测与处理3.2.1 缺失值类型与业务影响3.2.1.1 缺失值的三种形态3.2.1.2 业务影响分级 3.2.2 缺失值…...