计算机本科论文 网站建设,电子商务网站建设策划报告,sem和seo有什么区别,安全月考评哪个网站做文章目录 1.UDP通信概述2. UDP 单播和广播2.1 UDP 通信实例程序功能2.2 主窗口类定义和构造函数2.3 UDP通信的实现2.4 源码2.4.1 可视化UI设计2.4.2 mainwindow.h2.4.3 mainwindow.cpp 1.UDP通信概述
UDP(User Datagram Protocol#xff0c;用户数据报协议)是轻量的、不可靠的… 文章目录 1.UDP通信概述2. UDP 单播和广播2.1 UDP 通信实例程序功能2.2 主窗口类定义和构造函数2.3 UDP通信的实现2.4 源码2.4.1 可视化UI设计2.4.2 mainwindow.h2.4.3 mainwindow.cpp 1.UDP通信概述
UDP(User Datagram Protocol用户数据报协议)是轻量的、不可靠的、面向数据报 (datagram)、无连接的协议它可以用于对可靠性要求不高的场合。与 TCP 通信不同两个程序之间进行 UDP 通信无需预先建立持久的 socket 连接UDP 每次发送数据报都需要指定目标地址和端口(如图14-6 所示)。 QUdpSocket 类用于实现 UDP 通信它从 QAbstractSocket 类继承因而与 QTcpSocket 共享大部分的接口函数。主要区别是 QUdpSocket 以数据报传输数据而不是以连续的数据流。发送数据报使用函数QUdpSocket::writeDatagram()数据报的长度一般少于512 字节每个数据报包含发送者和接收者的 IP 地址和端口等信息。 要进行 UDP 数据接收要用 QUdpSocket::bind()函数先绑定一个端口用于接收传入的数据报。当有数据报传入时会发射readyRead()信号使用readDatagram()函数来读取接收到的数据报。
UDP 消息传送有单播、广播、组播三种模式其示意图如图 14-7 所示 单播 (unicast)模式一个 UDP 客户端发出的数据报只发送到另一个指定地址和端口的UDP 客户端是一对一的数据传输。 广播(broadcast)模式一个 UDP 客户端发出的数据报在同一网络范围内其他所有的UDP 客户端都可以收到。QUdpSocket 支持IPv4 广播。广播经常用于实现网络发现的协议。要获取广播数据只需在数据报中指定接收端地址为 QHostAddress::Broadcast一般的广播地址是 255.255.255.255 组播 (multicast)模式也称为多播。UDP 客户端加入到另一个组播IP 地址指定的多播组成员向组播地址发送的数据报组内成员都可以接收到类似于 QQ 群的功能。QUdpSocket::joinMulticastGroup()函数实现加入多播组的功能加入多播组后UDP 数据的收发和UDP数据收发方法一样。
使用广播和多播模式UDP 可以实现一些比较灵活的通信功能而 TCP 通信只有单播模式没有广播和多播模式。所以UDP 通信虽然不能保证数据传输的准确性但是具有灵活性一般的即时通信软件都是基于UDP 通信的。
QUdpSocket 类从QAbstractSocket 继承而来但是又定义了较多新的功能函数用于实现 UDP特有的一些功能如数据报读写和多播通信功能。QUdpSocket 没有定义新的信号。QUdpSocket的主要功能函数见表 14-6 (包括从 QAbstractSocket 继承的函数省略了函数中的 const 关键字省略了缺省参数) 在单播、广播和多播模式下UDP 程序都是对等的不像 TCP 通信那样分为客户端和服务器端。多播和广播的实现方式基本相同只是数据报的目标IP 地址设置不同多播模式需要加入多播组实现方式有较大差异。 为分别演示这三种 UDP 通信模式本节设计了两个实例。Samp14_3 实例演示 UDP 单播和广播通信Samp14_4实例演示UDP组播通信。
2. UDP 单播和广播
2.1 UDP 通信实例程序功能
实例程序 samp14_3 实现 UDP 单播和广播其主窗口是继承自 QMainWindow 的类界面用UI 设计器设计。程序可以进行 UDP 数据报的发送和接收samp14_3 的两个运行实例之间可以进行 UDP 通信这两个实例可以运行在同一台计算机上也可以运行在不同的计算机上。图 14-8和图14-9是samp14_3两个实例在一台计算机上运行时通信的界面。 在同一台计算机上运行时两个运行实例需要绑定不同的端口例如实例A 定端口 1200,实例B绑定端口 3355。实例A 向实例 B 发送数据报时需要指定实例 B 所在主机的IP 地址、绑定端口作为目标地址和目标端口(为了准确识别相应的客户端与上篇中元组的概念很相似)这样实例 B 才能接收到数据报。 如果两个实例在不同计算机上运行则可以使用相同的端口因为 IP 地址不同了不会导致绑定时发生冲突。一般的UDP 通信程序都是在不同的计算机上运行的约定一个固定的端口作为通信端口。
2.2 主窗口类定义和构造函数 主窗口是基于QMainWindow 的类 MainWindow界面采用 UI 设计器设计。MainWindow类的定义如下(省略了 UI 设计器为 actions 和按钮生成的槽函数声明):
#ifndef MAINWINDOW_H
#define MAINWINDOW_H#include QMainWindow#include QUdpSocket
#include QLabelnamespace Ui {
class MainWindow;
}class MainWindow : public QMainWindow
{Q_OBJECTprivate:QLabel *LabSocketState;//socket状态显示标签QUdpSocket *udpSocket;//QString getLocalIP();//获取本机IP地址public:explicit MainWindow(QWidget *parent 0);~MainWindow();private slots:
//自定义槽函数void onSocketStateChange(QAbstractSocket::SocketState socketState);void onSocketReadyRead();//读取socket传入的数据
...private:Ui::MainWindow *ui;
};#endif // MAINWINDOW_HQUdpSocket 类型的私有变量 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);LabSocketStatenew QLabel(QString::fromLocal8Bit(Socket状态));//LabSocketState-setMinimumWidth(200);ui-statusBar-addWidget(LabSocketState);QString localIPgetLocalIP();//本机IPthis-setWindowTitle(this-windowTitle()QString::fromLocal8Bit(----本机IP)localIP);ui-comboTargetIP-addItem(localIP);udpSocketnew QUdpSocket(this);//用于与连接的客户端通讯的QTcpSocketconnect(udpSocket,SIGNAL(stateChanged(QAbstractSocket::SocketState)),this,SLOT(onSocketStateChange(QAbstractSocket::SocketState)));onSocketStateChange(udpSocket-state());connect(udpSocket,SIGNAL(readyRead()),this,SLOT(onSocketReadyRead()));
}onSocketStateChange(udpSocket-state());代码与上篇TCP通信程序里的完全一样。
2.3 UDP通信的实现
要实现UDP 数据的接收必须先用QUdpSocket::bind()函数绑定一个端口用于监听传入的数据报解除绑定则使用 abort()函数。程序主窗口上的“绑定端口”和“解除绑定”按钮的响应代码如下:
void MainWindow::on_actStart_triggered()
{//绑定端口quint16 portui-spinBindPort-value(); //本机UDP端口if (udpSocket-bind(port))//绑定端口成功{ui-plainTextEdit-appendPlainText(QString::fromLocal8Bit(**已成功绑定));ui-plainTextEdit-appendPlainText(QString::fromLocal8Bit(**绑定端口)QString::number(udpSocket-localPort()));ui-actStart-setEnabled(false);ui-actStop-setEnabled(true);}elseui-plainTextEdit-appendPlainText(QString::fromLocal8Bit(**绑定失败));
}void MainWindow::on_actStop_triggered()
{//解除绑定udpSocket-abort(); //不能解除绑定ui-actStart-setEnabled(true);ui-actStop-setEnabled(false);ui-plainTextEdit-appendPlainText(QString::fromLocal8Bit(**已解除绑定));
}绑定端口后socket 的状态变为已绑定状态“BoundState”解除绑定后状态变为未连接状态UnconnectedState” 发送点对点消息和广播消息都使用 QUdpSocket:: writeDatagram()函数窗口上“发送消息和“广播消息”两个按钮的代码如下:
void MainWindow::on_btnSend_clicked()
{//发送消息 按钮QString targetIPui-comboTargetIP-currentText(); //目标IPQHostAddress targetAddr(targetIP);quint16 targetPortui-spinTargetPort-value();//目标portQString msgui-editMsg-text();//发送的消息内容QByteArray strmsg.toUtf8();udpSocket-writeDatagram(str,targetAddr,targetPort); //发出数据报ui-plainTextEdit-appendPlainText(QString::fromLocal8Bit([out] )msg);ui-editMsg-clear();ui-editMsg-setFocus();
}void MainWindow::on_btnBroadcast_clicked()
{ //广播消息 按钮quint16 targetPortui-spinTargetPort-value(); //目标端口QString msgui-editMsg-text();QByteArray strmsg.toUtf8();udpSocket-writeDatagram(str,QHostAddress::Broadcast,targetPort);ui-plainTextEdit-appendPlainText(QString::fromLocal8Bit([broadcast] )msg);ui-editMsg-clear();ui-editMsg-setFocus();
}使用writeDatagram()函数向一个目标用户发送消息时需要指定目标地址和端口。
在广播消息时只需将目标地址更换为一个特殊地址即广播地址 OHostAddress::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 strdatagram.data();QString peer[From peerAddr.toString():QString::number(peerPort)] ;ui-plainTextEdit-appendPlainText(peerstr);}
}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 字节数组因为本程序只是传输字符串所以简单地将其转换为字符串即可。如果传输的是自定义格式的字符串或二进制数据需要对接收到的数据进行解析。
2.4 源码
2.4.1 可视化UI设计 2.4.2 mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H#include QMainWindow#include QUdpSocket
#include QLabelnamespace Ui {
class MainWindow;
}class MainWindow : public QMainWindow
{Q_OBJECTprivate:QLabel *LabSocketState;//socket状态显示标签QUdpSocket *udpSocket;//QString getLocalIP();//获取本机IP地址public:explicit MainWindow(QWidget *parent 0);~MainWindow();private slots:
//自定义槽函数void onSocketStateChange(QAbstractSocket::SocketState socketState);void onSocketReadyRead();//读取socket传入的数据
//void on_actStart_triggered();void on_actStop_triggered();void on_actClear_triggered();void on_btnSend_clicked();void on_actHostInfo_triggered();void on_btnBroadcast_clicked();private:Ui::MainWindow *ui;
};#endif // MAINWINDOW_H
2.4.3 mainwindow.cpp
#include mainwindow.h
#include ui_mainwindow.h
#include QtNetworkQString MainWindow::getLocalIP()
{QString hostNameQHostInfo::localHostName();//本地主机名QHostInfo hostInfoQHostInfo::fromName(hostName);QString localIP;QListQHostAddress addListhostInfo.addresses();//if (!addList.isEmpty())for (int i0;iaddList.count();i){QHostAddress aHostaddList.at(i);if (QAbstractSocket::IPv4ProtocolaHost.protocol()){localIPaHost.toString();break;}}return localIP;
}MainWindow::MainWindow(QWidget *parent) :QMainWindow(parent),ui(new Ui::MainWindow)
{ui-setupUi(this);LabSocketStatenew QLabel(QString::fromLocal8Bit(Socket状态));//LabSocketState-setMinimumWidth(200);ui-statusBar-addWidget(LabSocketState);QString localIPgetLocalIP();//本机IPthis-setWindowTitle(this-windowTitle()QString::fromLocal8Bit(----本机IP)localIP);ui-comboTargetIP-addItem(localIP);udpSocketnew QUdpSocket(this);//用于与连接的客户端通讯的QTcpSocketconnect(udpSocket,SIGNAL(stateChanged(QAbstractSocket::SocketState)),this,SLOT(onSocketStateChange(QAbstractSocket::SocketState)));onSocketStateChange(udpSocket-state());connect(udpSocket,SIGNAL(readyRead()),this,SLOT(onSocketReadyRead()));
}MainWindow::~MainWindow()
{udpSocket-abort();delete udpSocket;delete ui;
}void MainWindow::onSocketStateChange(QAbstractSocket::SocketState socketState)
{switch(socketState){case QAbstractSocket::UnconnectedState:LabSocketState-setText(QString::fromLocal8Bit(scoket状态UnconnectedState));break;case QAbstractSocket::HostLookupState:LabSocketState-setText(QString::fromLocal8Bit(scoket状态HostLookupState));break;case QAbstractSocket::ConnectingState:LabSocketState-setText(QString::fromLocal8Bit(scoket状态ConnectingState));break;case QAbstractSocket::ConnectedState:LabSocketState-setText(QString::fromLocal8Bit(scoket状态ConnectedState));break;case QAbstractSocket::BoundState:LabSocketState-setText(QString::fromLocal8Bit(scoket状态BoundState));break;case QAbstractSocket::ClosingState:LabSocketState-setText(QString::fromLocal8Bit(scoket状态ClosingState));break;case QAbstractSocket::ListeningState:LabSocketState-setText(QString::fromLocal8Bit(scoket状态ListeningState));}
}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 strdatagram.data();QString peer[From peerAddr.toString():QString::number(peerPort)] ;ui-plainTextEdit-appendPlainText(peerstr);}
}void MainWindow::on_actStart_triggered()
{//绑定端口quint16 portui-spinBindPort-value(); //本机UDP端口if (udpSocket-bind(port))//绑定端口成功{ui-plainTextEdit-appendPlainText(QString::fromLocal8Bit(**已成功绑定));ui-plainTextEdit-appendPlainText(QString::fromLocal8Bit(**绑定端口)QString::number(udpSocket-localPort()));ui-actStart-setEnabled(false);ui-actStop-setEnabled(true);}elseui-plainTextEdit-appendPlainText(QString::fromLocal8Bit(**绑定失败));
}void MainWindow::on_actStop_triggered()
{//解除绑定udpSocket-abort(); //不能解除绑定ui-actStart-setEnabled(true);ui-actStop-setEnabled(false);ui-plainTextEdit-appendPlainText(QString::fromLocal8Bit(**已解除绑定));
}void MainWindow::on_actClear_triggered()
{ui-plainTextEdit-clear();
}void MainWindow::on_btnSend_clicked()
{//发送消息 按钮QString targetIPui-comboTargetIP-currentText(); //目标IPQHostAddress targetAddr(targetIP);quint16 targetPortui-spinTargetPort-value();//目标portQString msgui-editMsg-text();//发送的消息内容QByteArray strmsg.toUtf8();udpSocket-writeDatagram(str,targetAddr,targetPort); //发出数据报ui-plainTextEdit-appendPlainText(QString::fromLocal8Bit([out] )msg);ui-editMsg-clear();ui-editMsg-setFocus();
}void MainWindow::on_actHostInfo_triggered()
{//本机地址 按钮QString hostNameQHostInfo::localHostName();//本地主机名ui-plainTextEdit-appendPlainText(QString::fromLocal8Bit(本机主机名)hostName\n);QHostInfo hostInfoQHostInfo::fromName(hostName);QListQHostAddress addListhostInfo.addresses();//if (!addList.isEmpty())for (int i0;iaddList.count();i){QHostAddress aHostaddList.at(i);if (QAbstractSocket::IPv4ProtocolaHost.protocol()){QString IPaHost.toString();ui-plainTextEdit-appendPlainText(QString::fromLocal8Bit(本机IP地址)aHost.toString());if (ui-comboTargetIP-findText(IP)0)ui-comboTargetIP-addItem(IP);}}}void MainWindow::on_btnBroadcast_clicked()
{ //广播消息 按钮quint16 targetPortui-spinTargetPort-value(); //目标端口QString msgui-editMsg-text();QByteArray strmsg.toUtf8();udpSocket-writeDatagram(str,QHostAddress::Broadcast,targetPort);ui-plainTextEdit-appendPlainText(QString::fromLocal8Bit([broadcast] )msg);ui-editMsg-clear();ui-editMsg-setFocus();
}