网站首页 > 技术文章 正文
实例程序实现UDP单播和广播,其主窗口是继承自QMainWindow的类,界面用UI设计器设计。程序可以进行UDP数据报的发送和接收,两个运行实例之间可以进行UDP通信,这两个实例可以运行在同一台计算机上,也可以运行在不同的计算机上。下图是两个实例在一台计算机上运行时通信的界面。
在同一台计算机上运行时,两个运行实例需要绑定不同的端口,例如实例A绑定端口1200, 实例B绑定端口3355。实例A向实例B发送数据报时,需要指定实例B所在主机的IP地址、绑定端口作为目标地址和目标端口,这样实例B才能接收到数据报。
如果两个实例在不同计算机上运行,则可以使用相同的端口,因为IP地址不同了,不会导致绑定时发生冲突。一般的UDP通信程序都是在不同的计算机上运行的,约定一个固定的端口作为通信端口。
1. 主窗口类定义和构造函数
主窗口是基于QMainWindow的类MainWindow,界面用UI设计器设计。MainWindow类的定义如下(省略了UI设计器为actions和按钮生成的槽函数声明):
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = nullptr);
~MainWindow();
private:
Ui::MainWindow *ui;
QLabel *LabSocketState;//状态栏标签
QUdpSocket *udpSocket;
QString getLocalIP();//获取本机 IP 地址
private slots:
//自定义槽函数
void onSocketStateChange(QAbstractSocket::SocketState socketState);
void onSocketReadyRead();//读取 socket 传入的数据
};
QUdpSocket类型的私有变量udpSocket是用于UDP通信的socket。
定义了两个自定义槽函数,onSocketStateChange()与udpSocket的stateChange()信号关联,用于显示udpSocket当前的状态;onSocketReadyRead()信号与udpSocket的readyRead()信号关联,用于读取缓冲区的数据报。
MainWindow的构造函数主要完成udpSocket的创建、信号与槽函数的关联,代码如下:
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent), ui(new Ui::MainWindow)
{
ui->setupUi(this);
LabSocketState=new QLabel("Socket 状态:");
LabSocketState->setMinimumWidth(200);
ui->statusbar->addWidget(LabSocketState);
QString localIP=getLocalIP() ; //本机 IP
this->setWindowTitle(this->windowTitle()+" 本机 IP: "+localIP);
ui->comboIP->addItem(localIP);
udpSocket=new QUdpSocket(this);
connect(udpSocket,SIGNAL(stateChanged(QAbstractSocket::SocketState)),
this,SLOT(onSocketStateChange(QAbstractSocket::SocketState)));
onSocketStateChange(udpSocket->state());
connect(udpSocket,SIGNAL(readyRead()),this,SLOT(onSocketReadyRead()));
}
槽函数onSocketStateChange()的功能与前面文章中的TCP通信程序里的完全一样,不再显示其具体代码。
2. UDP端口绑定
要实现UDP数据的接收,必须先用QUdpSocket::bind()函数绑定一个端口,用于监听传入的数据报,解除绑定则使用abort()函数。程序主窗口上的“绑定端口”和“解除绑定”按钮的响应代码如下:
void MainWindow::on_actionStart_triggered()
{//绑定端口
quint16 port=ui->spinBindPort->value(); //本机 UDP 端口
if(udpSocket->bind(port)){ //绑定端口成功
ui->plainTextEdit->appendPlainText("**已成功绑定");
ui->plainTextEdit->appendPlainText("**绑定端口: "+QString::number(udpSocket->localPort()));
ui->actionStart->setEnabled(false);
ui->actionStop->setEnabled(true);
}else{
ui->plainTextEdit->appendPlainText("**绑定失败");
}
}
void MainWindow::on_actionStop_triggered()
{//解除绑定
udpSocket->abort(); //解除绑定
ui->actionStart->setEnabled(true);
ui->actionStop->setEnabled(false);
ui->plainTextEdit->appendPlainText("**己解除绑定");
}
绑定端口后,socket的状态变为己绑定状态“BoundState”,解除绑定后状态变为未连接状态“UnconnectedState”。
3.UDP通信实现
发送点对点消息和广播消息都使用QUdpSocket::writeDatagram()函数,窗口上“发送消息”和“广播消息”两个按钮的代码如下:
void MainWindow::on_btnSend_clicked()
{ //发送消息按钮
QString targetIP=ui->comboIP->currentText(); //目标IP
QHostAddress targetAddr(targetIP);
quint16 targetPort=ui->spinTargetPort->value(); //目标 port
QString msg=ui->edtMsg->text();//发送的消息内容
QByteArray str=msg.toUtf8();
udpSocket->writeDatagram(str, targetAddr, targetPort); //发出数据报
ui->plainTextEdit->appendPlainText("[out] "+msg);
ui->edtMsg->clear();
ui->edtMsg->setFocus();
}
void MainWindow::on_btnBroadcast_clicked()
{//广播消息按钮
quint16 targetPort=ui->spinTargetPort->value(); //目标端口
QString msg=ui->edtMsg->text();
QByteArray str=msg.toUtf8();
udpSocket->writeDatagram(str,QHostAddress::Broadcast,targetPort);
ui->plainTextEdit->appendPlainText("[broadcast] "+msg);
ui->edtMsg->clear();
ui->edtMsg->setFocus();
}
使用writeDatagram()函数向一个目标用户发送消息时,需要指定目标地址和端口。
在广播消息时,只需将目标地址更换为一个特殊地址,即广播地址QHostAddress::Broadcast, 一般是255.255.255.255。
QUdpSocket发送的数据报是QByteArray类型的字节数组,数据报的长度一般不超过512字节。数据报的内容可以是文本字符串,也可以自定义格式的二进制数据,文本字符串无需以换行符结束。
QUdpSocket接收到数据报后发射readyRead()信号,在关联的槽函数onSocketReadyRead()里读取缓冲区的数据报,代码如下:
void MainWindow::onSocketReadyRead()
{//读取收到的数据报
while(udpSocket->hasPendingDatagrams()){
QByteArray datagram;
datagram.resize(udpSocket->pendingDatagramSize());
QHostAddress peerAddr;
quint16 peerPort;
udpSocket->readDatagram(datagram.data(), datagram.size(), &peerAddr, &peerPort);
QString str=datagram.data();
QString peer="[From "+peerAddr.toString()+":"+QString::number(peerPort)+"] ";
ui->plainTextEdit->appendPlainText(peer+str);
}
}
hasPendingDatagrams()表示是否有待读取的传入数据报。pendingDatagramSize()返回待读取数据报的字节数。readDatagram()函数用于读取数据报的内容,其函数原型为:
qint64 QUdpSocket::readDatagram(char *data, qint64 maxSize, QHostAddress *address = nullptr, quint16 *port = nullptr)
输入参数data和maxSize是必须的,表示最多读取maxSize字节的数据到变量data里。address和port变量是可选的,用于获取数据报来源的地址和端口。上面的代码中使用了完整的参数形式,从而可以获得数据报来源的地址peerAddr和端口peerPort。如果无需获取来源地址和端口,可以采用简略形式,即:
udpSocket->readDatagram(datagram.data(),datagram.size());
读取的数据报内容是QByteArray字节数组,因为本程序只是传输字符串,所以简单地将其转换为字符串即可。如果传输的是自定义格式的字符串或二进制数据,需要对接收到的数据进行解析。
————————————————
觉得有用的话请关注点赞,谢谢您的支持!
对于本系列文章相关示例完整代码有需要的朋友,可关注并在评论区留言!
猜你喜欢
- 2024-12-24 Excel VBA 用户窗体设置/一步一步代你设计EXCEL用户+密码登录界面
- 2024-12-24 家庭收支理财管理系统 Access数据库系统课程设计制作实例
- 2024-12-24 Qt 2D绘图:图形视图框架的事件处理与传播
- 2024-12-24 MFC界面库BCGControlBar v32.1 - 可视化管理器和主题升级
- 2024-12-24 DJYGUI系列文章九:GDD消息系统 gd信息
- 2024-12-24 Qt设备识别(简单的密钥生成器) qt设备管理系统
- 2024-12-24 Access开发的《财务经济管理系统》
- 2024-12-24 初级开发人员告诉我:OO 设计模式太复杂而且没用
- 2024-12-24 Qt入门阶段之事件 qtc间期延长的临床意义
- 2024-12-24 Access窗体查询制作步骤第二课 access窗体怎么做查询按钮
- 02-21走进git时代, 你该怎么玩?_gits
- 02-21GitHub是什么?它可不仅仅是云中的Git版本控制器
- 02-21Git常用操作总结_git基本用法
- 02-21为什么互联网巨头使用Git而放弃SVN?(含核心命令与原理)
- 02-21Git 高级用法,喜欢就拿去用_git基本用法
- 02-21Git常用命令和Git团队使用规范指南
- 02-21总结几个常用的Git命令的使用方法
- 02-21Git工作原理和常用指令_git原理详解
- 最近发表
- 标签列表
-
- cmd/c (57)
- c++中::是什么意思 (57)
- sqlset (59)
- ps可以打开pdf格式吗 (58)
- phprequire_once (61)
- localstorage.removeitem (74)
- routermode (59)
- vector线程安全吗 (70)
- & (66)
- java (73)
- org.redisson (64)
- log.warn (60)
- cannotinstantiatethetype (62)
- js数组插入 (83)
- resttemplateokhttp (59)
- gormwherein (64)
- linux删除一个文件夹 (65)
- mac安装java (72)
- reader.onload (61)
- outofmemoryerror是什么意思 (64)
- flask文件上传 (63)
- eacces (67)
- 查看mysql是否启动 (70)
- java是值传递还是引用传递 (58)
- 无效的列索引 (74)