Qt之UDP通信
Posted 风间琉璃•
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Qt之UDP通信相关的知识,希望对你有一定的参考价值。
目录
一、UDP简介
UDP(User Datagram Protocol 即用户数据报协议)是一个轻量级的,不可靠的,面向数据
报的无连接协议。由于 UDP 的特性:它不属于连接型协议,因而具有资源消耗小,处理速度快的优点,所以通常音频、视频和普通数据在传送时使用 UDP 较多,因为它们即使偶尔丢失一两个数据包,也不会对接收结果产生太大影响。
UDP 通信示意图如下:
UDP 消息传送有三种模式,分别是单播、广播和组播三种模式。
①单播(unicast): 单播用于两个主机之间的端对端通信,需要知道对方的 IP 地址与端口
②广播(broadcast): 广播 UDP 与单播 UDP 的区别就是 IP 地址不同,广播一般使用广播地址
255.255.255.255,将消息发送到在同一广播(也就是局域网内同一网段) 网络上的每个主机
注意:本地广播信息是不会被路由器转发,所以如果一个服务端在win,另外一个客户端在虚拟机说,这时就需要配置虚拟机的端口转发,这样虚拟机才会连得上服务器。
③组播(multicast): 组播(多点广播),也称为多播,将网络中同一业务类型主机进行了逻辑上的分组,进行数据收发的时候其数据仅仅在同一分组中进行,其他的主机没有加入此分组不能收发对应的数据。
在广域网上广播的时候,其中的交换机和路由器只向需要获取数据的主机复制并转发数据。主机可以向路由器请求加入或退出某个组,网络中的路由器和交换机有选择地复制并传输数据,将数据仅仅传输给组内的主机。多播的这种功能,可以一次将数据发送到多个主机,又能保证不影响其他不需要(未加入组)的主机的其他通信。
注意: 单播一样和多播是允许在广域网即 Internet 上进行传输的,而广播仅仅在同一局域网上才能进行。
二、QUdpSocket类
QT 的 socket 类之间的关系:
QUdpSocket 类提供了一个 UDP 套接字。 QUdpSocket 是 QAbstractSocket 的子类,允许发
送和接收 UDP 数据报。
常用API函数
①构造函数
QUdpSocket::QUdpSocket(QObject *parent = Q_NULLPTR)
②如果至少有一个数据报在等待被读取,则返回true,否则返回false。
bool QUdpSocket::hasPendingDatagrams() const
③服务器绑定端口
bool bind(const QHostAddress &address, quint16 port = 0, BindMode mode = DefaultForPlatform);
④返回第一个待处理的UDP数据报的大小Byte。如果没有可用的数据报,该函数返回-1。
qint64 QUdpSocket::pendingDatagramSize() const
⑤接收数据
qint64 QUdpSocket::readDatagram(char *data, qint64 maxSize, QHostAddress *address = Q_NULLPTR, quint16 *port = Q_NULLPTR)
接收一个不大于maxSize字节的数据报并将其存储在data中。发送者的主机地址和端口存储在*address和*port中(除非指针为0)。成功时返回数据报的大小;否则返回-1。
如果maxSize太小,数据报的其余部分将被丢失。为了避免数据丢失,在试图读取数据报之前,应调用pendingDatagramSize()来确定未决数据报的大小。如果maxSize为0,数据报将被丢弃。
⑥发送数据
qint64 QUdpSocket::writeDatagram(const char *data, qint64 size, const QHostAddress &address, quint16 port)
将数据报以大小的方式发送到端口端口的主机地址。成功时返回发送的字节数,否则返回-1。
数据报总是被写成一个块。数据报的最大尺寸与平台高度相关,但可以低至8192字节。如果数据报太大,这个函数将返回-1,error()将返回DatagramTooLargeError。
一般来说,发送大于512字节的数据报是不利的,因为即使它们被成功发送,在到达最终目的地之前,它们很可能被IP层分割开来。
三、UDP服务器
1.创建QUdpSocket对象
mSocket = new QUdpSocket(this);
②绑定地址和端口号
msocket->bind(ip,端口号);
③收到数据时,会触发readyRead()信号,自定义readPendingDatagrams()进行读取数据;
connect(msocket,&QUdpSocket::readyRead, this,&Widget::readPendingDatagrams);
④在while循环中读取数据,只要有数据,就一直读取并处理。
void Server::readPendingDatagrams()
while (udpSocket->hasPendingDatagrams()) //数据报等待被读取
//数据缓冲区QByteArray arr;
//调整缓冲区的大小和收到的数据大小一致
arr.resize(mSocket->bytesAvailable()); //接收数据
mSocket->readDatagram(arr.data(),arr.size(),&addr,&port);
//将arr.data转为字符串即可
QString str = arr.data();
通信(先接收) 收到数据会触发信号readyRead, 通过QUdpSocket对象的readDatagram函数来接收数据 。
readyRead()信号在数据报到达时发出。在这种情况下, hasPendingDatagrams()返回 true。调用 pendingDatagramSize()来获取第一个待处理数据报的大小,并调用 readDatagram()接收数据。
注意:当接收到readyRead()信号时,一个传入的数据报应该被读取,否则这个信号将不会被发送到下一个数据报。
⑤发送数据
qint64 QUdpSocket::writeDatagram(const char *data, qint64 size, const QHostAddress &address, quint16 port)
若是广播消息,与单播消息不同的是将目标 IP 地址换成了广播地址,一般广播地址为 255.255.255.255,也可以使用QHostAddress::Broadcast获取广播地址。
QHostAddress peerAddr = QHostAddress::Broadcast;
只需要将客户端发送数据:writeDatagram的IP地址改为广播地址即可。
四、UDP客户端
①创建QUdpSocket对象
mSocket = new QUdpSocket(this);
②发送数据到指定的地址和端口号
writeDatagram(数据,接收方ip,接收方端口号);
发送的数据要是QByteArray类型,Qt中将字符串转为QByteArray可以使用.toUtf8函数。
五、代码
1.udp服务端
头文件
#ifndef UDPSERVER_H
#define UDPSERVER_H
#include <QWidget>
#include <QtNetwork>
QT_BEGIN_NAMESPACE
namespace Ui class UdpServer;
QT_END_NAMESPACE
class UdpServer : public QWidget
Q_OBJECT
public:
UdpServer(QWidget *parent = nullptr);
~UdpServer();
private slots:
void on_pushButton_start_clicked();
void on_pushButton_send_clicked();
void readPendingDatagrams();
private:
Ui::UdpServer *ui;
//Udp服务器
QUdpSocket *mSocket;
//通信的ip和端口,用于获取发送者的 IP 和端口
QHostAddress addr;
quint16 port;
;
#endif // UDPSERVER_H
源文件
#include "udpserver.h"
#include "ui_udpserver.h"
UdpServer::UdpServer(QWidget *parent)
: QWidget(parent)
, ui(new Ui::UdpServer)
ui->setupUi(this);
UdpServer::~UdpServer()
delete ui;
//启动
void UdpServer::on_pushButton_start_clicked()
//1.创建QUdpSocket对象
mSocket = new QUdpSocket(this);
//2.连接接收数据信号和槽
QObject::connect(mSocket,&QUdpSocket::readyRead,this,&UdpServer::readPendingDatagrams);
//3.绑定
mSocket->bind(QHostAddress::Any,ui->spinBox->value());
//连接回车发送的信号和槽
QObject::connect(ui->lineEdit,&QLineEdit::returnPressed,this,&UdpServer::on_pushButton_send_clicked);
//禁止端口号和启动按钮
ui->spinBox->setEnabled(false);
ui->pushButton_start->setEnabled(false);
void UdpServer::on_pushButton_send_clicked()
//获取发送的数据
QByteArray arr = ui->lineEdit->text().toUtf8();
//发送
mSocket->writeDatagram(arr,addr,port);
//显示发送的内容
ui->textBrowser->insertPlainText("send:"+QString(arr)+"\\n");
//情况lineEdit
ui->lineEdit->clear();
void UdpServer::readPendingDatagrams()
//数据缓冲区
QByteArray arr;
while(mSocket->hasPendingDatagrams())
//调整缓冲区的大小和收到的数据大小一致
arr.resize(mSocket->bytesAvailable());
//接收数据
mSocket->readDatagram(arr.data(),arr.size(),&addr,&port);
//显示
ui->textBrowser->insertPlainText(addr.toString()+":"+QString(arr)+"\\n");
//使能发送按钮和编辑框
ui->lineEdit->setEnabled(true);
ui->pushButton_send->setEnabled(true);
2.udp客户端
头文件
#ifndef UDPCILENT_H
#define UDPCILENT_H
#include <QWidget>
#include <QtNetwork>
QT_BEGIN_NAMESPACE
namespace Ui class UdpCilent;
QT_END_NAMESPACE
class UdpCilent : public QWidget
Q_OBJECT
public:
UdpCilent(QWidget *parent = nullptr);
~UdpCilent();
private slots:
void on_pushButton_send_clicked();
void readPendingDatagrams();
private:
Ui::UdpCilent *ui;
//UDP客户端
QUdpSocket *mSocket;
;
#endif // UDPCILENT_H
源文件
#include "udpcilent.h"
#include "ui_udpcilent.h"
UdpCilent::UdpCilent(QWidget *parent)
: QWidget(parent)
, ui(new Ui::UdpCilent)
ui->setupUi(this);
//1.创建QUdpSocket
mSocket = new QUdpSocket(this);
//2.通信(接收)
QObject::connect(mSocket,&QUdpSocket::readyRead,this,&UdpCilent::readPendingDatagrams);
//连接回车发送的信号和槽
QObject::connect(ui->lineEdit_send,&QLineEdit::returnPressed,this,&UdpCilent::on_pushButton_send_clicked);
UdpCilent::~UdpCilent()
delete ui;
//发送
void UdpCilent::on_pushButton_send_clicked()
//获取发送的数据
QByteArray arr = ui->lineEdit_send->text().toUtf8();
//发送
//mSocket->writeDatagram(arr,QHostAddress(ui->lineEdit_ip->text()),ui->spinBox->value());
mSocket->writeDatagram(arr,QHostAddress::Broadcast,ui->spinBox->value());
//显示发送的内容
ui->textBrowser->insertPlainText("send:"+QString(arr)+"\\n");
//情况lineEdit
ui->lineEdit_send->clear();
void UdpCilent::readPendingDatagrams()
QHostAddress addr; //用于获取发送者的 IP 和端口
quint16 port;
//数据缓冲区
QByteArray arr;
while(mSocket->hasPendingDatagrams())
//调整缓冲区的大小和收到的数据大小一致
arr.resize(mSocket->bytesAvailable());
//接收数据
mSocket->readDatagram(arr.data(),arr.size(),&addr,&port);
//显示
ui->textBrowser->insertPlainText(addr.toString()+":"+QString(arr)+"\\n");
结果:
QT学习笔记(13) QT下的UDP通信
一、UDP通信
UDP通信没有明确的服务器端和客户端之分
TCP通信像是打电话(必须要接通才能通信),UDP通信像是写信(不管能不能收到都发送出去)
首先需要QUdpSOcket套接字,然后绑定bind()端口号和ip
如果对方发送过来数据,套接字自动触发readyRead()方法
套接字QUdpSOcket通过readDatagram()和writeDatagram()方法读取和写入数据
二、示例代码如下:
QT_HelloWorld12.pro
1 #------------------------------------------------- 2 # 3 # Project created by QtCreator 2017-08-31T14:54:40 4 # 5 #------------------------------------------------- 6 7 QT += core gui network 8 9 greaterThan(QT_MAJOR_VERSION, 4): QT += widgets 10 11 TARGET = QT_HelloWorld12 12 TEMPLATE = app 13 14 15 SOURCES += main.cpp\\ 16 widget.cpp \\ 17 widget_2.cpp 18 19 HEADERS += widget.h \\ 20 widget_2.h 21 22 FORMS += widget.ui \\ 23 widget_2.ui
main.cpp
1 #include "widget.h" 2 #include <QApplication> 3 #include "widget_2.h" 4 5 int main(int argc, char *argv[]) 6 { 7 QApplication a(argc, argv); 8 Widget w; 9 w.show(); 10 widget_2 w2; 11 w2.show(); 12 13 return a.exec(); 14 }
widget.h
1 #ifndef WIDGET_H 2 #define WIDGET_H 3 4 #include <QWidget> 5 #include <QUdpSocket>//UDP套接字 6 7 namespace Ui { 8 class Widget; 9 } 10 11 class Widget : public QWidget 12 { 13 Q_OBJECT 14 15 public: 16 explicit Widget(QWidget *parent = 0); 17 ~Widget(); 18 19 void dealMsg();//槽函数,用来处理传递过来的数据 20 21 private slots: 22 void on_pushButton_send_clicked(); 23 24 private: 25 Ui::Widget *ui; 26 27 QUdpSocket *udpSocket; 28 }; 29 30 #endif // WIDGET_H
widget_2.h
1 #ifndef WIDGET_2_H 2 #define WIDGET_2_H 3 4 #include <QWidget> 5 #include <QUdpSocket>//UDP套接字 6 namespace Ui { 7 class widget_2; 8 } 9 10 class widget_2 : public QWidget 11 { 12 Q_OBJECT 13 14 public: 15 explicit widget_2(QWidget *parent = 0); 16 ~widget_2(); 17 void dealMsg();//槽函数,用来处理传递过来的数据 18 19 private slots: 20 void on_pushButton_send_clicked(); 21 22 private: 23 Ui::widget_2 *ui; 24 QUdpSocket *udpSocket; 25 }; 26 27 #endif // WIDGET_2_H
widget.cpp
1 #include "widget.h" 2 #include "ui_widget.h" 3 #include <QUdpSocket> 4 #include <QHostAddress> 5 6 Widget::Widget(QWidget *parent) : 7 QWidget(parent), 8 ui(new Ui::Widget) 9 { 10 ui->setupUi(this); 11 setWindowTitle("8888"); 12 //分配空间,指定父对象 13 udpSocket = new QUdpSocket(this); 14 //绑定 端口号 15 udpSocket->bind(8888); 16 17 //当对方成功发送数据过来,自动触发readyRead() 18 connect(udpSocket,&QUdpSocket::readyRead,this,&Widget::dealMsg); 19 } 20 21 Widget::~Widget() 22 { 23 delete ui; 24 } 25 void Widget::dealMsg() 26 { 27 //读取对方发送的内容 28 char buf[1024]; 29 QHostAddress address;//对方地址 30 quint16 port;//对方端口 31 qint64 len = udpSocket->readDatagram(buf,sizeof(buf),&address,&port); 32 if(len>0) 33 { 34 //格式化 [127.0.0.1:8888] ******* 35 QString str = QString("[%1:%2] %3") 36 .arg(address.toString().arg(port).arg(buf)); 37 //将内容放到编辑区 38 ui->textEdit_write->setText(str); 39 } 40 } 41 42 void Widget::on_pushButton_send_clicked() 43 { 44 //获取对方的IP和端口 45 QString ip = ui->lineEdit_ip->text(); 46 qint16 port = ui->lineEdit_port->text().toInt(); 47 //获取编辑区内容 48 QString str = ui->textEdit_write->toPlainText(); 49 //给指定的IP发送数据 50 udpSocket->writeDatagram(str.toUtf8(),QHostAddress(ip),port); 51 52 }
widget_2.cpp
1 #include "widget_2.h" 2 #include "ui_widget_2.h" 3 4 widget_2::widget_2(QWidget *parent) : 5 QWidget(parent), 6 ui(new Ui::widget_2) 7 { 8 ui->setupUi(this); 9 setWindowTitle("9999"); 10 //分配空间,指定父对象 11 udpSocket = new QUdpSocket(this); 12 //绑定 端口号 13 //udpSocket->bind(9999); 14 udpSocket->bind(QHostAddress::AnyIPv4,8888);//如果是组播,则不能直接只绑定端口号(网段所有地址) 15 //加入某个组播 16 //组播地址是D类地址 17 udpSocket->joinMulticastGroup(QHostAddress("224.0.0.2")); 18 //udpSocket->leaveMulticastGroup(QHostAddress("224.0.0.2"));//离开组播 19 20 //当对方成功发送数据过来,自动触发readyRead() 21 connect(udpSocket,&QUdpSocket::readyRead,this,&widget_2::dealMsg); 22 } 23 24 widget_2::~widget_2() 25 { 26 delete ui; 27 } 28 void widget_2::dealMsg() 29 { 30 //读取对方发送的内容 31 char buf[1024]; 32 QHostAddress address;//对方地址 33 //如果地址是255.255.255.255,则表示广播,在整个局域网内 34 quint16 port;//对方端口 35 qint64 len = udpSocket->readDatagram(buf,sizeof(buf),&address,&port); 36 if(len>0) 37 { 38 //格式化 [127.0.0.1:8888] ******* 39 QString str = QString("[%1:%2] %3") 40 .arg(address.toString().arg(port).arg(buf)); 41 //将内容放到编辑区 42 ui->textEdit_write->setText(str); 43 } 44 } 45 46 void widget_2::on_pushButton_send_clicked() 47 { 48 //获取对方的IP和端口 49 QString ip = ui->lineEdit_ip->text(); 50 qint16 port = ui->lineEdit_port->text().toInt(); 51 //获取编辑区内容 52 QString str = ui->textEdit_write->toPlainText(); 53 //给指定的IP发送数据 54 udpSocket->writeDatagram(str.toUtf8(),QHostAddress(ip),port); 55 }
widget.ui和widget_2.ui的界面设计
以上是关于Qt之UDP通信的主要内容,如果未能解决你的问题,请参考以下文章