当前位置:   article > 正文

Qt —UDP通信QUdpSocket 简介 +案例_qt udp

qt udp

1. UDP通信概述


   UDP是无连接、不可靠、面向数据报(datagram)的协议,可以应用于对可靠性要求不高的场合。与TCP通信不同,UDP通信无需预先建立持久的socket连接,UDP每次发送数据报都需要指定目标地址和端口。

   QUdpSocket以数据报传输数据,而不是以连续的数据流。发送数据报使用函数         QUdpSocket::writeDatagram()数据报的长度一般少于512字节,每个数据报包含发送者和接收者的IP地址和端口等信息。
     UDP数据接收,首先要使用QUdpSocket::bind()绑定一个端口,绑定端口后,socket的状态会变为已绑定状态“BoundState”。

当有数据报传入时,QudpSocket会自动发射readyRead()信号,在其槽函数中使用QUdpSocket::readDatagram()进行数据读取。abort()为解除绑定,解除后socket状态变为未连接状态“UnconnectedState”。

2. UDP消息传送的三种模式

单播模式(unicast):一个UDP客户端发送数据报到指定地址和端口的另一UDP客户端,是一对一的数据传输。

广播模式(broadcast):一个UDP客户端发出的数据报,在同一网络范围内其他所有的UDP客户端都可以收到。QUdpSocket支持IPv4广播。需要在数据报中指定接收端地址为QHostAddress::Broadcast,一般的广播地址是255.255.255.255

组播模式(multicast):UDP客户端加入到另一个组播IP地址的多播组,成员向组播地址发送的数据报,其加入组播的所有成员都可以接收到,类似于QQ群功能。QUdpSocket::joinMulticastGroup()函数实现加入多播组的功能。

QUdpSocket::leaveMulticastGroup()函数实现

在单播、广播和多播模式下,UDP程序都是对等的,不像TCP通信分为客户端和服务端。
TCP通信只有单播模式。UDP通信虽然不能保证数据传输的准确性,但是具有灵活性,一般的即时通信软件都是基于UDP通信的。

3. QUdpSocket类的接口函数

bool bind(quint16 port = 0)  为UDP通信绑定一个端口

qint64 writeDatagram(QByteArray& datagram, QHostAddress& host, quint16 port) 向目标地址和端口的UDP客户端发送数据报,返回成功发送的字节数,数据报的长度一般不超过512字节。

bool hasPendingDatagrams()     当至少有一个数据报需要读取时,返回true

qint64 pendingDatagramSize()    返回第一个待读取的数据报的大小

qint64 readDatagram(char* data, qint64 maxSize) 读取一个数据报,返回成功读取的字节数

qint64 readDatagram(char* data, qint64 maxSize, QHostAddress* address, quint16* port) 读取一个数据报,返回成功读取的字节数。发送方的主机地址和端口存储在*address和*port中(除非指针为0)

bool joinMulticastGroup(QHostAddress& groupAddress)      加入一个多播组

bool leaveMulticastGroup(QHostAddress& groupAddress)    离开一个多播组

void abort() 终止当前连接并重置套接字。通常在析构函数中写入。与disconnectFromHost()不同,该函数立即关闭套接字,丢弃写入缓冲区中的任何挂起数据。
原文链接:https://blog.csdn.net/WL0616/article/details/129050373

4.UDP对话小案例

实现发送和接收端互相发信息,类似QQ  (界面使用UI设计)

4.1.接收端

receiver.h

  1. #ifndef RECESIVER_H
  2. #define RECESIVER_H
  3. #include <QWidget>
  4. #include <QUdpSocket>
  5. QT_BEGIN_NAMESPACE
  6. namespace Ui { class Recesiver; }
  7. QT_END_NAMESPACE
  8. class Recesiver : public QWidget
  9. {
  10. Q_OBJECT
  11. public:
  12. Recesiver(QWidget *parent = nullptr);
  13. ~Recesiver();
  14. private slots:
  15. void on_pushButton_2_clicked(); //启动槽函数
  16. void start();
  17. void on_pushButton_clicked();
  18. private:
  19. Ui::Recesiver *ui;
  20. QUdpSocket *receiver;
  21. };
  22. #endif // RECESIVER_H

receiver.cpp

  1. #include "recesiver.h"
  2. #include "ui_recesiver.h"
  3. Recesiver::Recesiver(QWidget *parent)
  4. : QWidget(parent)
  5. , ui(new Ui::Recesiver)
  6. {
  7. ui->setupUi(this);
  8. setWindowTitle(QStringLiteral("接收端"));
  9. ui->lineEdit->setText("127.0.0.1");
  10. receiver =new QUdpSocket(this);
  11. }
  12. Recesiver::~Recesiver()
  13. {
  14. delete ui;
  15. }
  16. //接收信息
  17. void Recesiver::start()
  18. {
  19. QByteArray datagram;
  20. datagram.resize(receiver->pendingDatagramSize()); //接收到的数据的长度
  21. receiver->readDatagram(datagram.data(),datagram.size());
  22. ui->textEdit->append(QStringLiteral("对方:")+datagram);
  23. }
  24. //启动按钮(发送端发送信息给接收端)
  25. void Recesiver::on_pushButton_2_clicked()
  26. {
  27. receiver->bind(ui->lineEdit_2->text().toInt());//设置端口号将其转为整型
  28. connect(receiver,&QUdpSocket::readyRead,this,[&](){start();});
  29. ui->pushButton_2->setEnabled(false);
  30. ui->lineEdit_2->setEnabled(false);
  31. }
  32. //发送按钮(接收端发送信息给发送端)
  33. void Recesiver::on_pushButton_clicked()
  34. {
  35. QByteArray datagram=ui->textEdit_2->toPlainText().toUtf8(); //在输入端输入发送的内容
  36. receiver->writeDatagram(datagram.data(),datagram.size(),QHostAddress(ui->lineEdit->text()),ui->lineEdit_3->text().toInt());
  37. //qint64 writeDatagram(const char *data, qint64 len, const QHostAddress &host, quint16 port);
  38. //发送数据,大小,发送主机的ip,发送主机的端口号
  39. ui->textEdit->append(QStringLiteral("本机:")+ui->textEdit_2->toPlainText());//发送信息的具体内容在发送端的聊天记录里能体现
  40. ui->textEdit_2->clear();
  41. }

4.2发送端

sender.h

  1. #ifndef SENDER_H
  2. #define SENDER_H
  3. #include <QWidget>
  4. #include <QUdpSocket>
  5. QT_BEGIN_NAMESPACE
  6. namespace Ui { class Sender; }
  7. QT_END_NAMESPACE
  8. class Sender : public QWidget
  9. {
  10. Q_OBJECT
  11. public:
  12. Sender(QWidget *parent = nullptr);
  13. ~Sender();
  14. void start2();
  15. private slots:
  16. void on_pushButton_clicked();
  17. private:
  18. Ui::Sender *ui;
  19. QUdpSocket *sender;
  20. };
  21. #endif // SENDER_H

sender.cpp

  1. #include "sender.h"
  2. #include "ui_sender.h"
  3. Sender::Sender(QWidget *parent)
  4. : QWidget(parent)
  5. , ui(new Ui::Sender)
  6. {
  7. ui->setupUi(this);
  8. sender=new QUdpSocket(this);
  9. setWindowTitle(QStringLiteral("发送端"));
  10. //sender
  11. ui->lineEdit_3->setText("888");
  12. sender->bind(ui->lineEdit_3->text().toInt());//绑定端口号
  13. connect(sender,&QUdpSocket::readyRead,this,[&](){start2();});
  14. //start2();
  15. }
  16. void Sender::start2()
  17. {
  18. QByteArray datagram;
  19. datagram.resize(sender->pendingDatagramSize()); //接收到的数据的长度
  20. sender->readDatagram(datagram.data(),datagram.size());
  21. ui->textEdit->append(QStringLiteral("对方:")+datagram);
  22. }
  23. Sender::~Sender()
  24. {
  25. delete ui;
  26. }
  27. //发送按钮 发送信息给接收端
  28. void Sender::on_pushButton_clicked()
  29. {
  30. QByteArray datagram=ui->textEdit_2->toPlainText().toUtf8(); //在输入端输入发送的内容
  31. sender->writeDatagram(datagram.data(),datagram.size(),QHostAddress(ui->lineEdit->text()),ui->lineEdit_2->text().toInt());
  32. //qint64 writeDatagram(const char *data, qint64 len, const QHostAddress &host, quint16 port);
  33. //发送数据,大小,发送主机的ip,发送主机的端口号
  34. ui->textEdit->append(QStringLiteral("本机:")+ui->textEdit_2->toPlainText());//发送信息的具体内容在发送端的聊天记录里能体现
  35. ui->textEdit_2->clear();
  36. }

结果:

(ps:对话有点搞笑,哈哈哈)

5.4. UDP单播和广播代码示例

4.1 测试说明

  本实例实现UDP通信的单播和广播。两个实例可以运行在同一台计算机上,也可以运行在不同的计算机上。
这里的两个实例是运行在同一台计算机上,需要注意,在同一台计算机上运行时,两个实例需要绑定不同的端口。例如实例A绑定端口1600,实例B绑定端口3200,实例A向实例B发送数据报时,需要指定实例B的端口,这样实例B才能收到数据。
如果两个实例在不同的计算机上运行,则端口可以一样,因为IP地址不同了,不会导致绑定时发生冲突。一般的UDP通信程序都是在不同的计算机上运行的,约定一个固定的端口作为通信端口。

4.2主要程序

用UI设计器件和代码化UI分别设计

ui设计器设计:

.h

  1. #ifndef MAINWINDOW_H
  2. #define MAINWINDOW_H
  3. #include <QMainWindow>
  4. #include <QLabel>
  5. #include <QAbstractSocket>
  6. #include <QUdpSocket>
  7. QT_BEGIN_NAMESPACE
  8. namespace Ui { class MainWindow; }
  9. QT_END_NAMESPACE
  10. class MainWindow : public QMainWindow
  11. {
  12. Q_OBJECT
  13. public:
  14. MainWindow(QWidget *parent = nullptr);
  15. ~MainWindow();
  16. void handleEvents(); // 信号与槽处理
  17. private:
  18. Ui::MainWindow *ui;
  19. QUdpSocket *udpScoket;
  20. QLabel *labstateScoket; //Scoket状态栏标签
  21. QString getLocalIp(); //获取本机的IP地址
  22. private slots:
  23. void slotActBindPort(); //绑定端口
  24. void slotActUnbindPort(); //解除端口
  25. void slotActClearText(); //清空文本框
  26. void slotActQuit(); //退出
  27. void slotSocketReadyRead(); //读取socket传入的数据
  28. void slotSocketStateChanged(QAbstractSocket::SocketState socketState);
  29. void on_pushButton_clicked(); //发送信息
  30. void on_pushButton_2_clicked(); //广播信息
  31. };
  32. #endif // MAINWINDOW_H

.cpp

  1. #include "mainwindow.h"
  2. #include "ui_mainwindow.h"
  3. #include <QTextEdit>
  4. #include <QMessageBox>
  5. #include <QHostInfo>
  6. #include <QAction>
  7. #include <QPushButton>
  8. MainWindow::MainWindow(QWidget *parent)
  9. : QMainWindow(parent)
  10. , ui(new Ui::MainWindow)
  11. {
  12. ui->setupUi(this);
  13. QString localIP = getLocalIp(); //获取本机的IP地址
  14. this->setWindowTitle(this->windowTitle() + "---IP:" + localIP);
  15. ui->comboBox->addItem(localIP); //在目标地址中添加IP
  16. udpScoket = new QUdpSocket(this);
  17. //状态栏
  18. labstateScoket = new QLabel(QStringLiteral("socket状态:"), this);
  19. labstateScoket->setMinimumWidth(150);
  20. ui->statusBar->addWidget(labstateScoket);
  21. handleEvents();
  22. ui->spinBox->setMinimum(1); //设置绑定端口的最大最小值和当前的端口值
  23. ui->spinBox->setMaximum(65535);
  24. ui->spinBox->setValue(1600);
  25. ui->spinBox_2->setMinimum(1); //设置目标端口的最大最小值和当前的端口值
  26. ui->spinBox_2->setMaximum(65535);
  27. ui->spinBox_2->setValue(3200);
  28. }
  29. MainWindow::~MainWindow()
  30. {
  31. udpScoket->abort();
  32. delete udpScoket;
  33. udpScoket = nullptr;
  34. delete ui;
  35. }
  36. // 信号与槽处理
  37. void MainWindow::handleEvents()
  38. {
  39. connect(ui->action, &QAction::triggered, this, &MainWindow::slotActBindPort );
  40. connect(ui->action_2, &QAction::triggered, this, &MainWindow::slotActUnbindPort);
  41. connect(ui->action_3, &QAction::triggered, this, &MainWindow::slotActClearText);
  42. connect(ui->action_4, &QAction::triggered, this, &MainWindow::slotActQuit);
  43. connect(ui->pushButton, &QPushButton::clicked, this, &MainWindow::on_pushButton_clicked);
  44. connect(ui->pushButton_2, &QPushButton::clicked, this, &MainWindow::on_pushButton_2_clicked);
  45. connect(udpScoket, &QUdpSocket::stateChanged, this, &MainWindow::slotSocketStateChanged);
  46. connect(udpScoket, &QUdpSocket::readyRead, this, &MainWindow::slotSocketReadyRead);
  47. }
  48. QString MainWindow::getLocalIp()
  49. {
  50. QString hostName = QHostInfo::localHostName();//获取本机主机名
  51. QHostInfo hostInfo = QHostInfo::fromName(hostName); //返回主机名的IP地址
  52. QString localIP = "";
  53. QList<QHostAddress> addrList = hostInfo.addresses(); //主机IP地址列表
  54. if (!addrList.isEmpty())
  55. {
  56. for (int i = 0; i < addrList.size(); i++)
  57. {
  58. QHostAddress addr = addrList.at(i);
  59. if (QAbstractSocket::IPv4Protocol == addr.protocol())
  60. {
  61. localIP = addr.toString();
  62. break;
  63. }
  64. }
  65. }
  66. return localIP;
  67. }
  68. //绑定端口
  69. void MainWindow::slotActBindPort()
  70. {
  71. quint16 port=ui->spinBox->value(); //绑定本机UDp端口
  72. if(udpScoket->bind(port))
  73. {
  74. ui->textEdit->append(QStringLiteral("**已经绑定成功"));
  75. ui->textEdit->append(QStringLiteral("**绑定端口:")+QString::number(udpScoket->localPort()));
  76. ui->action->setEnabled(false); //开始绑定失效
  77. ui->action_2->setEnabled(true);//解除绑定使能
  78. }
  79. else
  80. ui->textEdit->append(QStringLiteral("绑定失败"));
  81. }
  82. //解除端口
  83. void MainWindow::slotActUnbindPort()
  84. {
  85. udpScoket->abort();//解除绑定
  86. ui->action->setEnabled(true); //开始绑定失效
  87. ui->action_2->setEnabled(false);//解除绑定使能
  88. ui->textEdit->append(QStringLiteral("**已经解除绑定"));
  89. }
  90. //清空文本框
  91. void MainWindow::slotActClearText()
  92. {
  93. ui->textEdit->clear();
  94. }
  95. //退出
  96. void MainWindow::slotActQuit()
  97. {
  98. QMessageBox::StandardButton button = QMessageBox::question(this, "", QStringLiteral("是否要退出?"));
  99. if (button == QMessageBox::StandardButton::Yes)
  100. this->close();
  101. }
  102. //读取socket传入的数据
  103. void MainWindow::slotSocketReadyRead()
  104. {
  105. while(udpScoket->hasPendingDatagrams()) //当有数据传入数据报
  106. {
  107. QByteArray datagtam;
  108. datagtam.resize(udpScoket->pendingDatagramSize());//读取数据报大小
  109. QHostAddress peerAddr; //在已连接的状态下,返回对方的socket的地址
  110. quint16 peerPort; //在已连接的状态下,返回对方的socket的端口
  111. udpScoket->readDatagram(datagtam.data(),datagtam.size(),&peerAddr,&peerPort);//读取数据报的内容
  112. //qint64 readDatagram(char *data, qint64 maxlen, QHostAddress *host = nullptr, quint16 *port = nullptr);
  113. QString str=datagtam.data();
  114. QString peer="[From "+peerAddr.toString()+":"+QString::number(peerPort)+"]";
  115. ui->textEdit->append( peer + str);
  116. }
  117. }
  118. //状态栏显示Socket变化情况
  119. void MainWindow::slotSocketStateChanged(QAbstractSocket::SocketState socketState)
  120. {
  121. switch (socketState)
  122. {
  123. case QAbstractSocket::UnconnectedState: labstateScoket->setText(QStringLiteral("socket状态:UnconnectedState")); break;
  124. case QAbstractSocket::HostLookupState: labstateScoket->setText(QStringLiteral("socket状态:HostLookupState")); break;
  125. case QAbstractSocket::ConnectingState: labstateScoket->setText(QStringLiteral("socket状态:ConnectingState")); break;
  126. case QAbstractSocket::ConnectedState: labstateScoket->setText(QStringLiteral("socket状态:ConnectedState")); break;
  127. case QAbstractSocket::BoundState: labstateScoket->setText(QStringLiteral("socket状态:BoundState")); break;
  128. case QAbstractSocket::ClosingState: labstateScoket->setText(QStringLiteral("socket状态:ClosingState")); break;
  129. default: break;
  130. }
  131. }
  132. //发送信息
  133. void MainWindow::on_pushButton_clicked()
  134. {
  135. QString msg = ui->lineEdit->text(); //发送信息
  136. QByteArray str = msg.toUtf8();
  137. QString targetIp = ui->comboBox->currentText(); //目标IP就是主机地址的ip
  138. QHostAddress targetAddr(targetIp);
  139. quint16 targetPort = ui->spinBox_2->value(); //目标端口
  140. udpScoket->writeDatagram(str, targetAddr, targetPort);//发出数据报
  141. ui->textEdit->append("[out] " + msg);
  142. ui->lineEdit->clear();
  143. ui->lineEdit->setFocus();
  144. }
  145. //广播信息
  146. void MainWindow::on_pushButton_2_clicked()
  147. {
  148. quint16 targetPort = ui->spinBox_2->value(); //目标端口
  149. QString msg=ui->lineEdit->text();
  150. QByteArray str = msg.toUtf8();
  151. udpScoket->writeDatagram(str, QHostAddress::Broadcast, targetPort);//发出数据报
  152. ui->textEdit->append("[broadcast] " + msg);
  153. ui->lineEdit->clear();
  154. ui->lineEdit->setFocus();
  155. }

代码化:

.h

  1. #ifndef MAINWINDOW_H
  2. #define MAINWINDOW_H
  3. #include <QAction>
  4. #include <QComboBox>
  5. #include <QGridLayout>
  6. #include <QHBoxLayout>
  7. #include <QHostInfo>
  8. #include <QLabel>
  9. #include <QLineEdit>
  10. #include <QMainWindow>
  11. #include <QMessageBox>
  12. #include <QPlainTextEdit>
  13. #include <QPushButton>
  14. #include <QSpinBox>
  15. #include <QUdpSocket>
  16. #include <QVBoxLayout>
  17. namespace Ui {
  18. class MainWindow;
  19. }
  20. class MainWindow : public QMainWindow {
  21. Q_OBJECT
  22. public:
  23. explicit MainWindow(QWidget* parent = 0);
  24. ~MainWindow();
  25. private slots:
  26. void slotActBindPort();
  27. void slotActUnbindPort();
  28. void slotActClearText();
  29. void slotActQuit();
  30. void slotSocketStateChanged(QAbstractSocket::SocketState socketState);
  31. void slotBtnSend();
  32. void slotBtnBroad();
  33. void slotSocketReadyRead(); //读取socket传入的数据
  34. private:
  35. Ui::MainWindow* ui;
  36. QAction* m_pActBindPort;
  37. QAction* m_pActUnbindPort;
  38. QAction* m_pActClearText;
  39. QAction* m_pActQuit;
  40. QWidget* m_pCentralWidget;
  41. QLabel* m_pLabBindPort;
  42. QLabel* m_PLabTargetAddr;
  43. QLabel* m_pLabTargetPort;
  44. QSpinBox* m_pSpinBindPort;
  45. QComboBox* m_pComboTargetAddr;
  46. QSpinBox* m_pSpinTargetPort;
  47. QLineEdit* m_pLineEdit;
  48. QPushButton* m_pBtnSend;
  49. QPushButton* m_pBtnBroad;
  50. QPlainTextEdit* m_pPlainText;
  51. QLabel* m_pLabState;
  52. QUdpSocket* m_pUdpSocket;
  53. QString getLocalIP();
  54. };
  55. #endif // MAINWINDOW_H

.cpp

  1. #include "mainwindow.h"
  2. #include <QToolBar>
  3. #include "ui_mainwindow.h"
  4. MainWindow::MainWindow(QWidget* parent) : QMainWindow(parent), ui(new Ui::MainWindow) {
  5. ui->setupUi(this);
  6. //this->setWindowIcon(QIcon(":/new/prefix1/res/TitleIcon.png"));
  7. this->setWindowTitle(QStringLiteral("UDP Send/Receiver"));
  8. //工具栏
  9. ui->toolBar->setToolButtonStyle(Qt::ToolButtonTextBesideIcon);
  10. m_pActBindPort = new QAction(QIcon(":/new/Check.png"), QStringLiteral("绑定端口"), this);
  11. m_pActUnbindPort = new QAction(QIcon(":/new/Break.png"), QStringLiteral("结束绑定"), this);
  12. m_pActClearText = new QAction(QIcon(":/new/remove.png"), QStringLiteral("清空文本"), this);
  13. m_pActQuit = new QAction(QIcon(":/new/back.png"), QStringLiteral("退出"), this);
  14. ui->toolBar->addAction(m_pActBindPort);
  15. ui->toolBar->addAction(m_pActUnbindPort);
  16. ui->toolBar->addSeparator();
  17. ui->toolBar->addAction(m_pActClearText);
  18. ui->toolBar->addSeparator();
  19. ui->toolBar->addAction(m_pActQuit);
  20. //界面布局
  21. m_pCentralWidget = new QWidget(this);
  22. QHBoxLayout* HLay1 = new QHBoxLayout;
  23. m_pLabBindPort = new QLabel(QStringLiteral("绑定端口"), m_pCentralWidget);
  24. m_pSpinBindPort = new QSpinBox(m_pCentralWidget);
  25. m_pSpinBindPort->setMinimum(1);
  26. m_pSpinBindPort->setMaximum(65535);
  27. m_pSpinBindPort->setValue(3200);
  28. m_PLabTargetAddr = new QLabel(QStringLiteral("目标地址"), m_pCentralWidget);
  29. m_pComboTargetAddr = new QComboBox(m_pCentralWidget);
  30. m_pLabTargetPort = new QLabel(QStringLiteral("目标端口"), m_pCentralWidget);
  31. m_pSpinTargetPort = new QSpinBox(m_pCentralWidget);
  32. m_pSpinTargetPort->setMinimum(1);
  33. m_pSpinTargetPort->setMaximum(65535);
  34. m_pSpinTargetPort->setValue(1600);
  35. HLay1->addWidget(m_pLabBindPort, 1, Qt::AlignRight);
  36. HLay1->addWidget(m_pSpinBindPort, 2);
  37. HLay1->addWidget(m_PLabTargetAddr, 1, Qt::AlignRight);
  38. HLay1->addWidget(m_pComboTargetAddr, 4);
  39. HLay1->addWidget(m_pLabTargetPort, 1, Qt::AlignRight);
  40. HLay1->addWidget(m_pSpinTargetPort, 2);
  41. QHBoxLayout* HLay2 = new QHBoxLayout;
  42. m_pLineEdit = new QLineEdit(m_pCentralWidget);
  43. m_pBtnSend = new QPushButton(QStringLiteral("发送消息"), m_pCentralWidget);
  44. m_pBtnBroad = new QPushButton(QStringLiteral("广播消息"), m_pCentralWidget);
  45. HLay2->addWidget(m_pLineEdit);
  46. HLay2->addWidget(m_pBtnSend);
  47. HLay2->addWidget(m_pBtnBroad);
  48. QVBoxLayout* VLay = new QVBoxLayout(m_pCentralWidget); //主布局必须设置parent,否则不会显示布局
  49. // QVBoxLayout* VLay = new QVBoxLayout();
  50. VLay->addLayout(HLay1);
  51. VLay->addLayout(HLay2);
  52. m_pPlainText = new QPlainTextEdit(m_pCentralWidget);
  53. VLay->addWidget(m_pPlainText);
  54. this->setCentralWidget(m_pCentralWidget);
  55. this->setLayout(VLay); //设置为窗体的主布。在指定了主布局的parent之后,这句话可有可无
  56. QString localIP = getLocalIP();
  57. this->setWindowTitle(this->windowTitle() + "---IP:" + localIP);
  58. m_pComboTargetAddr->addItem(localIP);
  59. m_pUdpSocket = new QUdpSocket(this);
  60. //状态栏
  61. m_pLabState = new QLabel(QStringLiteral("socket状态:"), this);
  62. m_pLabState->setMinimumWidth(150);
  63. ui->statusbar->addWidget(m_pLabState);
  64. // connect
  65. connect(m_pActBindPort, &QAction::triggered, this, &MainWindow::slotActBindPort);
  66. connect(m_pActUnbindPort, &QAction::triggered, this, &MainWindow::slotActUnbindPort);
  67. connect(m_pActClearText, &QAction::triggered, this, &MainWindow::slotActClearText);
  68. connect(m_pActQuit, &QAction::triggered, this, &MainWindow::slotActQuit);
  69. connect(m_pBtnSend, &QPushButton::clicked, this, &MainWindow::slotBtnSend);
  70. connect(m_pBtnBroad, &QPushButton::clicked, this, &MainWindow::slotBtnBroad);
  71. connect(m_pUdpSocket, &QUdpSocket::stateChanged, this, &MainWindow::slotSocketStateChanged);
  72. connect(m_pUdpSocket, &QUdpSocket::readyRead, this, &MainWindow::slotSocketReadyRead);
  73. }
  74. MainWindow::~MainWindow() {
  75. //m_pUdpSocket->abort();
  76. //delete m_pUdpSocket;
  77. //m_pUdpSocket = nullptr;
  78. delete ui;
  79. }
  80. void MainWindow::slotActBindPort()
  81. {
  82. quint16 port = m_pSpinBindPort->value(); //本机UDP端口
  83. if (m_pUdpSocket->bind(port)) {
  84. m_pPlainText->appendPlainText(QStringLiteral("**已成功绑定"));
  85. m_pPlainText->appendPlainText(QStringLiteral("绑定端口:") + QString::number(m_pUdpSocket->localPort()));
  86. //使能
  87. m_pActBindPort->setEnabled(false);
  88. m_pActUnbindPort->setEnabled(true);
  89. } else {
  90. m_pPlainText->appendPlainText(QStringLiteral("绑定失败"));
  91. }
  92. }
  93. void MainWindow::slotActUnbindPort() {
  94. m_pUdpSocket->abort(); //解除绑定
  95. m_pPlainText->appendPlainText(QStringLiteral("**已解除绑定"));
  96. m_pActBindPort->setEnabled(true);
  97. m_pActUnbindPort->setEnabled(false);
  98. }
  99. void MainWindow::slotActClearText() { m_pPlainText->clear(); }
  100. void MainWindow::slotActQuit() {
  101. QMessageBox::StandardButton button = QMessageBox::question(this, "", QStringLiteral("是否要退出?"));
  102. if (button == QMessageBox::StandardButton::Yes)
  103. this->close();
  104. }
  105. void MainWindow::slotSocketStateChanged(QAbstractSocket::SocketState socketState) {
  106. switch (socketState)
  107. {
  108. case QAbstractSocket::UnconnectedState: m_pLabState->setText(QStringLiteral("socket状态:UnconnectedState")); break;
  109. case QAbstractSocket::HostLookupState: m_pLabState->setText(QStringLiteral("socket状态:HostLookupState")); break;
  110. case QAbstractSocket::ConnectingState: m_pLabState->setText(QStringLiteral("socket状态:ConnectingState")); break;
  111. case QAbstractSocket::ConnectedState: m_pLabState->setText(QStringLiteral("socket状态:ConnectedState")); break;
  112. case QAbstractSocket::BoundState: m_pLabState->setText(QStringLiteral("socket状态:BoundState")); break;
  113. case QAbstractSocket::ClosingState: m_pLabState->setText(QStringLiteral("socket状态:ClosingState")); break;
  114. default: break;
  115. }
  116. }
  117. void MainWindow::slotBtnSend()
  118. {
  119. QString msg = m_pLineEdit->text();
  120. QByteArray str = msg.toUtf8();
  121. QString targetIp = m_pComboTargetAddr->currentText(); //目标IP
  122. QHostAddress targetAddr(targetIp);
  123. quint16 targetPort = m_pSpinTargetPort->value(); //目标端口
  124. m_pUdpSocket->writeDatagram(str, targetAddr, targetPort);
  125. m_pPlainText->appendPlainText("[out] " + msg);
  126. m_pLineEdit->clear();
  127. m_pLineEdit->setFocus();
  128. }
  129. void MainWindow::slotBtnBroad()
  130. {
  131. QString msg = m_pLineEdit->text();
  132. QByteArray str = msg.toUtf8();
  133. quint16 targetPort = m_pSpinTargetPort->value();
  134. m_pUdpSocket->writeDatagram(str, QHostAddress::Broadcast, targetPort);
  135. m_pPlainText->appendPlainText("[out] " + msg);
  136. m_pLineEdit->clear();
  137. m_pLineEdit->setFocus();
  138. }
  139. void MainWindow::slotSocketReadyRead()
  140. {
  141. while (m_pUdpSocket->hasPendingDatagrams())
  142. {
  143. QByteArray dataGram;
  144. dataGram.resize(m_pUdpSocket->pendingDatagramSize());
  145. QHostAddress peerAddress;
  146. quint16 peerPort;
  147. m_pUdpSocket->readDatagram(dataGram.data(), dataGram.size(), &peerAddress, &peerPort);
  148. QString str = dataGram.data();
  149. QString peer = "[From " + peerAddress.toString() + ":" + QString::number(peerPort) + "]";
  150. m_pPlainText->appendPlainText(peer + str);
  151. }
  152. }
  153. QString MainWindow::getLocalIP() {
  154. QString hostName = QHostInfo::localHostName();
  155. QHostInfo hostInfo = QHostInfo::fromName(hostName);
  156. QString localIP = "";
  157. QList<QHostAddress> addrList = hostInfo.addresses();
  158. if (!addrList.isEmpty()) {
  159. for (int i = 0; i < addrList.size(); i++) {
  160. QHostAddress addr = addrList.at(i);
  161. if (QAbstractSocket::IPv4Protocol == addr.protocol()) {
  162. localIP = addr.toString();
  163. break;
  164. }
  165. }
  166. }
  167. return localIP;
  168. }

结果: 

 发现自己用UI设计时,发送信息总会出现两个发送信号,并且有一个是空白的,暂时还没有找到问题所在之处,嘤嘤嘤~(/≧▽≦)/

5. UDP组播代码示例

5.1 组播的特性

组播报文的目的地址使用D类IP地址,D类地址不能出现在IP报文的地址字段。用同一个IP多播地址接收多播数据报的所有主机构成一个组,称为多播组(或组播组)。所有的信息接收者都加入一个组内,并且一旦加入后,流向组地址的数据报立即开始向接收者传输,组中的所有的成员都能接收到数据报。组中的成员是动态的,主机可以在任何人时间加入和离开组。

关于组播IP地址,有以下约定:

224.0.0.0 ~ 224.0.0.255   为预留的组播地址(永久组地址),地址224.0.0.0保留不做分配,其他地址供路由协议使用。
224.0.1.0 ~ 224.0.1.255   是公用组播地址,可以用于Internet。
224.0.2.0 ~ 238.255.255.255     为用户可用的组播地址(临时组地址),全网范围内有效。
239.0.0.0 ~ 239.255.255.255     为本地管理组播地址,仅在特定的本地范围内有效。所以,若是在家庭或办公室局域网内测试UDP组播功能,可以使用这些IP。
原文链接:https://blog.csdn.net/WL0616/article/details/129050373

常量定义

描述

QUdpSocket::ShareAddress

0x1

1、允许其他服务绑定同样的地址和端口
2、当多进程通过监听同一地址和端口,进而共享单个服务的负载时,将十分有用(例如:一个拥有几个预先建立的监听者的WEB服务器能够改善响应时间)。不过,由于任何服务都允许重新绑定(rebind),该选项应该引起某些安全上的考虑
3、需要注意的是,把该选项和ReuseAddressHint结合,也会允许你的服务重新绑定一个已存在的共享地址
4、在Unix上,该选项等同于SO_REUSEADDR;在Windows上,该选项被忽略

QUdpSocket::DontShareAddress

0x2

1、采用专有的方式绑定某个地址和端口,其他任何服务都不能再重新绑定

2、通过该选项,确保绑定成功,指定的服务将是地址和端口唯一监听者,就算是拥有ReuseAddressHint的服务也不允许重新绑定

3、在安全性上,该选项优于ShareAddress,但是在某些操作系统上需要管理员的权限才能运行

4、在Unix和Mac OS上,绑定地址和端口的默认行为是非共享,所以该选项会被忽略;在Windows上,等同于SO_EXCLUSIVEADDRUSE套接字选项

QUdpSocket::ReuseAddressHint

0x4

1、为QUdpSocke提供提示,即在地址和端口已经被其他套接字绑定的情况下,也应该试着重新绑定

2、在Unix上,该选项被忽略;在Windows上等同于SO_REUSEADDR 套接字选项

QUdpSocket::DefaultForPlatform

0x0

1、当前平台的默认选项

2、在Unix和Mac OS上,该选项等同于DontShareAddress + ReuseAddressHint;在Windows上等同于ShareAddress

5.2主要程序 

.h

  1. #ifndef MAINWINDOW_H
  2. #define MAINWINDOW_H
  3. #include <QAction>
  4. #include <QComboBox>
  5. #include <QHBoxLayout>
  6. #include <QHostInfo>
  7. #include <QLabel>
  8. #include <QLineEdit>
  9. #include <QMainWindow>
  10. #include <QMessageBox>
  11. #include <QPlainTextEdit>
  12. #include <QPushButton>
  13. #include <QRegExp>
  14. #include <QSpinBox>
  15. #include <QUdpSocket>
  16. #include <QVBoxLayout>
  17. namespace Ui {
  18. class MainWindow;
  19. }
  20. class MainWindow : public QMainWindow {
  21. Q_OBJECT
  22. public:
  23. explicit MainWindow(QWidget* parent = 0);
  24. ~MainWindow();
  25. private slots:
  26. void slotActJoinMulti();
  27. void slotActLeaveMulti();
  28. void slotActClearText();
  29. void slotActQuit();
  30. void slotSocketStateChanged(QAbstractSocket::SocketState socketState);
  31. void slotBtnMultiMsg();
  32. void slotReadyRead();
  33. private:
  34. Ui::MainWindow* ui;
  35. QAction* m_pActJoinMulti;
  36. QAction* m_pActLeaveMulti;
  37. QAction* m_pActClearText;
  38. QAction* m_pActQuit;
  39. QWidget* m_pCentralWidget;
  40. QLabel* m_pLabPort;
  41. QLabel* m_pLabAddr;
  42. QSpinBox* m_pSpinPort;
  43. QComboBox* m_pComboAddr;
  44. QLineEdit* m_pLineEdit;
  45. QPushButton* m_pBtnSendMulti;
  46. QPlainTextEdit* m_pPlainText;
  47. QLabel* m_pLabState;
  48. QUdpSocket* m_pUdpSocket;
  49. QHostAddress m_multicastAddr;
  50. QString getLocalIP();
  51. };
  52. #endif // MAINWINDOW_H

.cpp

  1. #include "mainwindow.h"
  2. #include "ui_mainwindow.h"
  3. MainWindow::MainWindow(QWidget* parent) : QMainWindow(parent), ui(new Ui::MainWindow) {
  4. ui->setupUi(this);
  5. this->setWindowTitle(QStringLiteral("UDP Multicast"));
  6. //工具栏
  7. ui->mainToolBar->setToolButtonStyle(Qt::ToolButtonTextBesideIcon);
  8. m_pActJoinMulti = new QAction(QIcon(":/new/prefix1/res/添加群组.png"), QStringLiteral("加入组播"), this);
  9. m_pActLeaveMulti = new QAction(QIcon(":/new/prefix1/res/退出群组.png"), QStringLiteral("退出组播"), this);
  10. m_pActClearText = new QAction(QIcon(":/new/prefix1/res/清空.png"), QStringLiteral("清空文本"), this);
  11. m_pActQuit = new QAction(QIcon(":/new/prefix1/res/退出.png"), QStringLiteral("退出"), this);
  12. ui->mainToolBar->addAction(m_pActJoinMulti);
  13. ui->mainToolBar->addAction(m_pActLeaveMulti);
  14. ui->mainToolBar->addSeparator();
  15. ui->mainToolBar->addAction(m_pActClearText);
  16. ui->mainToolBar->addSeparator();
  17. ui->mainToolBar->addAction(m_pActQuit);
  18. //界面布局
  19. m_pCentralWidget = new QWidget(this);
  20. m_pLabPort = new QLabel(QStringLiteral("组播端口"), m_pCentralWidget);
  21. m_pSpinPort = new QSpinBox(m_pCentralWidget);
  22. m_pSpinPort->setMinimum(1);
  23. m_pSpinPort->setMaximum(65535);
  24. m_pSpinPort->setValue(3200);
  25. m_pLabAddr = new QLabel(QStringLiteral("组播地址"), m_pCentralWidget);
  26. m_pComboAddr = new QComboBox(m_pCentralWidget);
  27. m_pComboAddr->setEditable(true); //下拉框可编辑输入
  28. m_pComboAddr->addItem("239.0.0.1");
  29. // 正则匹配 D类IP:224.0.0.0~239.255.255.255
  30. // .必须使用转义字符\,否则.会匹配任意字符
  31. // C++中"\"在字符串中表示要用"\\"
  32. // 是 - 不是 ~ ; 是[0-9]不是[0~9]
  33. QRegExp regExp("^(22[4-9]|23[0-9])(\\.((\\d)|([1-9]\\d)|(1\\d{2})|(2[0-4]\\d)|(25[0-5]))){3}$");
  34. QValidator* pValidator = new QRegExpValidator(regExp, this);
  35. m_pComboAddr->setValidator(pValidator);
  36. QHBoxLayout* HLay1 = new QHBoxLayout();
  37. HLay1->addWidget(m_pLabPort, 1, Qt::AlignRight);
  38. HLay1->addWidget(m_pSpinPort, 1);
  39. HLay1->addWidget(m_pLabAddr, 1, Qt::AlignRight);
  40. HLay1->addWidget(m_pComboAddr, 2);
  41. m_pLineEdit = new QLineEdit(m_pCentralWidget);
  42. m_pBtnSendMulti = new QPushButton(QStringLiteral("组播消息"), m_pCentralWidget);
  43. QHBoxLayout* HLay2 = new QHBoxLayout();
  44. HLay2->addWidget(m_pLineEdit, 4);
  45. HLay2->addWidget(m_pBtnSendMulti, 1);
  46. m_pPlainText = new QPlainTextEdit(m_pCentralWidget);
  47. QVBoxLayout* VLay = new QVBoxLayout(m_pCentralWidget);
  48. VLay->addLayout(HLay1);
  49. VLay->addLayout(HLay2);
  50. VLay->addWidget(m_pPlainText);
  51. this->setCentralWidget(m_pCentralWidget);
  52. this->setLayout(VLay);
  53. //状态栏
  54. m_pLabState = new QLabel(QStringLiteral("socket状态:"), this);
  55. m_pLabState->setMinimumWidth(150);
  56. ui->statusBar->addWidget(m_pLabState);
  57. QString str = getLocalIP();
  58. this->setWindowTitle(this->windowTitle() + "---IP:" + str);
  59. m_pUdpSocket = new QUdpSocket(this);
  60. m_pUdpSocket->setSocketOption(QAbstractSocket::MulticastTtlOption, 1);
  61. // connect
  62. connect(m_pActJoinMulti, &QAction::triggered, this, &MainWindow::slotActJoinMulti);
  63. connect(m_pActLeaveMulti, &QAction::triggered, this, &MainWindow::slotActLeaveMulti);
  64. connect(m_pActClearText, &QAction::triggered, this, &MainWindow::slotActClearText);
  65. connect(m_pActQuit, &QAction::triggered, this, &MainWindow::slotActQuit);
  66. connect(m_pUdpSocket, &QUdpSocket::stateChanged, this, &MainWindow::slotSocketStateChanged);
  67. connect(m_pBtnSendMulti, &QPushButton::clicked, this, &MainWindow::slotBtnMultiMsg);
  68. connect(m_pUdpSocket, &QUdpSocket::readyRead, this, &MainWindow::slotReadyRead);
  69. }
  70. MainWindow::~MainWindow() { delete ui; }
  71. void MainWindow::slotActJoinMulti() {
  72. QString ip = m_pComboAddr->currentText();
  73. m_multicastAddr = QHostAddress(ip);
  74. quint16 multicastPort = m_pSpinPort->value();
  75. if (m_pUdpSocket->bind(QHostAddress::AnyIPv4, multicastPort, QUdpSocket::ShareAddress)) {
  76. m_pUdpSocket->joinMulticastGroup(m_multicastAddr); //加入多播组
  77. m_pPlainText->appendPlainText("**加入组播成功");
  78. m_pPlainText->appendPlainText("**组播地址IP:" + ip);
  79. m_pPlainText->appendPlainText("**绑定端口:" + QString::number(multicastPort));
  80. m_pActJoinMulti->setEnabled(false);
  81. m_pActLeaveMulti->setEnabled(true);
  82. m_pComboAddr->setEditable(false);
  83. } else {
  84. m_pPlainText->appendPlainText("**绑定端口失败");
  85. }
  86. }
  87. void MainWindow::slotActLeaveMulti() {
  88. m_pUdpSocket->leaveMulticastGroup(m_multicastAddr); //退出组播
  89. m_pUdpSocket->abort(); //解除绑定
  90. m_pActJoinMulti->setEnabled(true);
  91. m_pActLeaveMulti->setEnabled(false);
  92. m_pComboAddr->setEnabled(true);
  93. m_pComboAddr->setEditable(true);
  94. m_pPlainText->appendPlainText("**已退出组播,解除端口绑定");
  95. }
  96. void MainWindow::slotActClearText() { m_pPlainText->clear(); }
  97. void MainWindow::slotActQuit() {
  98. QMessageBox::StandardButton button = QMessageBox::question(this, "", "是否退出?");
  99. if (QMessageBox::StandardButton::Yes == button) {
  100. this->close();
  101. }
  102. }
  103. void MainWindow::slotSocketStateChanged(QAbstractSocket::SocketState socketState) {
  104. // case并不包含所有的情况,因为没有写listening的情况,所以就需要写default
  105. switch (socketState) {
  106. case QAbstractSocket::UnconnectedState: m_pLabState->setText("socket状态:UnconnectedState"); break;
  107. case QAbstractSocket::HostLookupState: m_pLabState->setText("socket状态:HostLookupState"); break;
  108. case QAbstractSocket::ConnectingState: m_pLabState->setText("socket状态:ConnectingState"); break;
  109. case QAbstractSocket::ConnectedState: m_pLabState->setText("socket状态:ConnectedState"); break;
  110. case QAbstractSocket::BoundState: m_pLabState->setText("socket状态:BoundState"); break;
  111. case QAbstractSocket::ClosingState: m_pLabState->setText("socket状态:ClosingState"); break;
  112. default: break;
  113. }
  114. }
  115. void MainWindow::slotBtnMultiMsg() {
  116. QString msg = m_pLineEdit->text();
  117. QByteArray str = msg.toUtf8();
  118. quint16 multiPort = m_pSpinPort->value();
  119. m_pUdpSocket->writeDatagram(str, m_multicastAddr, multiPort);
  120. m_pPlainText->appendPlainText("[multicast] " + msg);
  121. m_pLineEdit->clear();
  122. m_pLineEdit->setFocus();
  123. }
  124. void MainWindow::slotReadyRead() {
  125. while (m_pUdpSocket->hasPendingDatagrams()) {
  126. QByteArray dataGram;
  127. dataGram.resize(m_pUdpSocket->pendingDatagramSize());
  128. QHostAddress peerAddr;
  129. quint16 peerPort;
  130. m_pUdpSocket->readDatagram(dataGram.data(), dataGram.size(), &peerAddr, &peerPort);
  131. QString str = dataGram.data();
  132. QString peer = "[From " + peerAddr.toString() + ":" + QString::number(peerPort) + "] ";
  133. m_pPlainText->appendPlainText(peer + str);
  134. qDebug() << m_pUdpSocket->peerAddress();
  135. qDebug() << m_pUdpSocket->localAddress().toString();
  136. qDebug() << m_pUdpSocket->localPort();
  137. }
  138. }
  139. QString MainWindow::getLocalIP() {
  140. QString localName = QHostInfo::localHostName();
  141. QHostInfo hostInfo = QHostInfo::fromName(localName);
  142. QList<QHostAddress> addrList = hostInfo.addresses();
  143. QString localIP = "";
  144. if (!addrList.isEmpty()) {
  145. for (int i = 0; i < addrList.size(); i++) {
  146. QHostAddress addr = addrList.at(i);
  147. if (QAbstractSocket::IPv4Protocol == addr.protocol()) {
  148. localIP = addr.toString();
  149. break;
  150. }
  151. }
  152. }
  153. return localIP;
  154. }

结果: 

原文链接:https://blog.csdn.net/WL0616/article/details/129050373 

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/繁依Fanyi0/article/detail/627102
推荐阅读
相关标签
  

闽ICP备14008679号