Qt之UDP通信

Posted 风间琉璃•

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Qt之UDP通信相关的知识,希望对你有一定的参考价值。

目录

一、UDP简介

二、QUdpSocket类

三、UDP服务器

四、UDP客户端

五、代码

1.udp服务端

2.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;

只需要将客户端发送数据:writeDatagramIP地址改为广播地址即可

四、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通信的主要内容,如果未能解决你的问题,请参考以下文章

Qt之UDP通信

Java之网络编程UDP和TCP

网络协议之UDP

Qt TCP/UDP 网络协议入门实操UDP通讯

我的QT Creator学习笔记(三十五)——网络编程之UDP与TCP

我的QT Creator学习笔记(三十五)——网络编程之UDP与TCP