UDP 通信介绍

  • UDP(User Datagram Protocol,用户数据报协议)
    UDP是一个轻量级、不可靠、面向数据报的、无连接的传输层协议,多用于可靠性要求不严格,不是非常重要的传输,如直播、视频会议等等。

  • Qt中QUdpSocket类继承自QAbstractSocket,用来发送和接收UDP数据报,”Socket”即套接字,套接字即IP地址+端口号。其中IP地址指定了网络中的一台主机,二端口号则指定了该主机上的一个应用程序,端口号只具有本地意义,使用套接字即可实现网络上的两个应用程序之间的通信。

  • QUdpSocket支持IPv4广播,要广播数据报,则只需发送到一个特殊的地址QHostAddress::Broadcast(即255.255.255.255),数据报一般建议发送字节数小于512字节。端口号选择1024-65535(1024以下的常用作保留端口号,如FTP常用端口号21,Telnet常用端口号23,DNS域名服务器常用端口53等)。

    在QT中使用QUdpSocket类用于实现UDP通信,它从QAbstractSocket类继承,因而与QTcpSocket共享大部分的接口函数。主要区别是QUdpSocket以数据报传输数据,而不是以连续的数据流。发送数据报使用函数QUdpSocket::writeDatagram(),数据报的长度一般不少于512字节,每个数据报包含发送者和接收者的IP地址和端口等信息。
    要进行UDP数据接收,要用QUdpSocket::bind()函数先绑定一个端口,用于接收传入的数据报。当有数据报传入时会发射readRead()信号,使用readDatagram()函数来读取接收的数据报。
    UDP消息传送有单播、广播、组播三种模式,其示意图如下:
    单播

    广播

    组播

单播和组播实现部分

实现部分–头文件

#ifndef EXTRANS_H
#define EXTRANS_H#include <QMainWindow>
#include <QLabel>
#include <QUdpSocket>
#include <QString>namespace Ui {class ExTrans;
}/*!* \class ExTrans 一个UDP的Deam测试,同时测试单播和广播* \brief 因为是在同一台电脑测试,所以IP相同,需要绑定两个不同端口的,这  样不会冲突;* 若是两台电脑进行测试,那么可以约定使用相同的端口号,使用不同的IP;来进行通讯*/class ExTrans : public QMainWindow
{Q_OBJECTpublic:explicit ExTrans(QWidget *parent = nullptr);~ExTrans();private slots:void on_actBind_triggered();     //绑定端口void on_actDisbind_triggered();  //解除绑定void on_actClean_triggered();    //清除文本信息void on_actQuit_triggered();     //关闭程序void on_btnUnicast_clicked();    //单播消息void on_btnBroadcast_clicked();  //广播消息void onSocketStateChange(QAbstractSocket::SocketState socketState);  //socket 状态发生变化void onSocketReadyRead();        //读取 socket 传入的数据private:QString getLocalIp();            //获取本机IPprivate:Ui::ExTrans *ui;QLabel* m_labSocketState;QUdpSocket* m_udpSocket;
};#endif // EXTRANS_H

实现部分–实现文件


#include "ExTrans.h"
#include "ui_ExTrans.h"#include <QHostInfo>ExTrans::ExTrans(QWidget *parent) :QMainWindow(parent),ui(new Ui::ExTrans)
{ui->setupUi(this);setWindowTitle("Udp通信:unicast(单播) + broadcast(广播)的使用");QString hostName = QHostInfo::localHostName();QString ip = getLocalIp();ui->plainTextEdit->appendPlainText("主机名称:" + hostName + "\n主机IP:" + ip + "\n");m_labSocketState = new QLabel("Socket状态:");m_labSocketState->setMinimumWidth(200);ui->statusBar->addWidget(m_labSocketState);ui->comboBoxIp->addItem(getLocalIp());m_udpSocket = new QUdpSocket(this);connect(m_udpSocket, SIGNAL(stateChanged(QAbstractSocket::SocketState)), this, SLOT(onSocketStateChange(QAbstractSocket::SocketState)));onSocketStateChange(m_udpSocket->state());connect(m_udpSocket, SIGNAL(readyRead()), this, SLOT(onSocketReadyRead()));
}ExTrans::~ExTrans()
{delete ui;
}
//绑定端口
void ExTrans::on_actBind_triggered()
{quint16 port = ui->spinBoxBind->value();   //本机UDP端口if (m_udpSocket->bind(port)) {   //端口绑定成功ui->plainTextEdit->appendPlainText("端口绑定成功:" + QString::number(m_udpSocket->localPort()));ui->actBind->setEnabled(false);ui->actDisbind->setEnabled(true);} else {ui->plainTextEdit->appendPlainText("端口绑定失败");}
}//解除绑定
void ExTrans::on_actDisbind_triggered()
{m_udpSocket->abort();  //断开,中止套接字ui->plainTextEdit->appendPlainText("端口解除绑定成功");ui->actBind->setEnabled(true);ui->actDisbind->setEnabled(false);
}//清除文本信息
void ExTrans::on_actClean_triggered()
{ui->plainTextEdit->clear();
}//关闭程序
void ExTrans::on_actQuit_triggered()
{close();
}//单播消息
void ExTrans::on_btnUnicast_clicked()
{QString targetIp = ui->comboBoxIp->currentText();QHostAddress targetAddr(targetIp);    //目标 Ipquint16 targetPort = ui->spinBoxPort->value();   //目标 portQString msg = ui->lineEdit->text();   //发送的消息QByteArray str = msg.toUtf8();m_udpSocket->writeDatagram(str, targetAddr, targetPort);  //发送数据报ui->plainTextEdit->appendPlainText(QString("[Send: ] %1").arg(msg));ui->lineEdit->clear();ui->lineEdit->setFocus();
}//广播消息
void ExTrans::on_btnBroadcast_clicked()
{quint16 targetPort = ui->spinBoxPort->value();   //目标 portQString msg = ui->lineEdit->text();   //发送的消息QByteArray str = msg.toUtf8();m_udpSocket->writeDatagram(str, QHostAddress::Broadcast, targetPort);  //发送 数据报 给所有IPui->plainTextEdit->appendPlainText(QString("[广播: ] %1").arg(msg));ui->lineEdit->clear();ui->lineEdit->setFocus();
}//socket 状态发生变化
void ExTrans::onSocketStateChange(QAbstractSocket::SocketState socketState)
{switch (socketState) {case QAbstractSocket::UnconnectedState:m_labSocketState->setText("socket状态:UnconnectedState");break;case QAbstractSocket::HostLookupState:m_labSocketState->setText("socket状态:HostLookupState");break;case QAbstractSocket::ConnectingState:m_labSocketState->setText("socket状态:ConnectingState");break;case QAbstractSocket::ConnectedState:m_labSocketState->setText("socket状态:ConnectedState");break;case QAbstractSocket::BoundState:m_labSocketState->setText("socket状态:BoundState");break;case QAbstractSocket::ClosingState:m_labSocketState->setText("socket状态:ClosingState");break;case QAbstractSocket::ListeningState:m_labSocketState->setText("socket状态:ListeningState");break;default:m_labSocketState->setText("socket状态:其他未知状态...");break;}
}//读取 socket 传入的数据
void ExTrans::onSocketReadyRead()
{while (m_udpSocket->hasPendingDatagrams()) {QByteArray datagram;datagram.resize(m_udpSocket->pendingDatagramSize());QHostAddress peerAddr;quint16 peerPort;m_udpSocket->readDatagram(datagram.data(), datagram.size(), &peerAddr, &peerPort);  //读取数据包,消息+来自的Ip和portQString str = datagram.data();QString peer = QString("[From: %1  %2] %3").arg(peerAddr.toString()).arg(QString::number(peerPort)).arg(str);ui->plainTextEdit->appendPlainText(peer);}
}//获取本机IP
QString ExTrans::getLocalIp()
{QString hostName = QHostInfo::localHostName();QHostInfo hostInfo = QHostInfo::fromName(hostName);QString Ip = "";if (hostInfo.addresses().isEmpty())return 0;foreach (QHostAddress addr, hostInfo.addresses()) {if (addr.protocol() == QAbstractSocket::IPv4Protocol) {Ip = addr.toString();break;}}return Ip;
}

上述代码实现了广播与单播的实现,下面为UI 部分

组播实现代码

实现部分–头文件


#ifndef EXMULTICAST_H
#define EXMULTICAST_H#include <QMainWindow>
#include <QUdpSocket>
#include <QLabel>
#include <QHostInfo>
#include <QHostAddress>namespace Ui {class ExMulticast;
}class ExMulticast : public QMainWindow
{Q_OBJECTpublic:explicit ExMulticast(QWidget *parent = nullptr);~ExMulticast();private slots:void on_actStart_triggered();void on_actStop_triggered();void on_actClear_triggered();void on_actQuit_triggered();void onSocketStateChange(QAbstractSocket::SocketState socketState);  //socket 状态发生变化void onSocketReadyRead();        //读取 socket 传入的数据void on_btnSend_clicked();private:QString getLocalIp();            //获取本机IPprivate:Ui::ExMulticast *ui;QUdpSocket* m_udpSocket;      //用于通讯的 socketQLabel* m_labSocketState;QHostAddress m_groupAddress;  //组播地址
};#endif // EXMULTICAST_H

实现部分–实现文件


#include "ExMulticast.h"
#include "ui_ExMulticast.h"ExMulticast::ExMulticast(QWidget *parent) :QMainWindow(parent),ui(new Ui::ExMulticast)
{ui->setupUi(this);setWindowTitle("Udp通信:multicate(组播)的使用");QString hostName = QHostInfo::localHostName();QString ip = getLocalIp();ui->plainTextEdit->appendPlainText("主机名称:" + hostName + "\n主机IP:" + ip + "\n");m_labSocketState = new QLabel("Socket状态:");m_labSocketState->setMinimumWidth(200);ui->statusBar->addWidget(m_labSocketState);ui->comboBoxIp->addItem(getLocalIp());m_udpSocket = new QUdpSocket(this);     //用于通讯使用的 Socket//Multicast路由层次,1表示只在同一局域网内//组播TTL: 生存时间,每跨1个路由会减1,多播无法跨过大多数路由所以为1//默认值是1,表示数据包只能在本地的子网中传送。m_udpSocket->setSocketOption(QAbstractSocket::MulticastTtlOption, 1);connect(m_udpSocket, SIGNAL(stateChanged(QAbstractSocket::SocketState)), this, SLOT(onSocketStateChange(QAbstractSocket::SocketState)));onSocketStateChange(m_udpSocket->state());connect(m_udpSocket, SIGNAL(readyRead()), this, SLOT(onSocketReadyRead()));
}ExMulticast::~ExMulticast()
{delete ui;
}void ExMulticast::on_actStart_triggered()
{QString ip = ui->comboBoxIp->currentText();m_groupAddress = QHostAddress(ip);quint16 port = ui->spinBoxBind->value();if (m_udpSocket->bind(QHostAddress::AnyIPv4, port, QUdpSocket::ShareAddress)) {  //绑定端口m_udpSocket->joinMulticastGroup(m_groupAddress);  //加入多播组ui->plainTextEdit->appendPlainText("[用户:" + getLocalIp() +"]  加入组播(组播地址:" + ip + " 端口:" + QString::number(port) + ")成功");ui->actStart->setEnabled(false);ui->actStop->setEnabled(true);} else {ui->plainTextEdit->appendPlainText("[用户:" + getLocalIp() +"]  加入组播(组播地址:" + ip + " 端口:" + QString::number(port) + ")失败");}
}void ExMulticast::on_actStop_triggered()
{m_udpSocket->leaveMulticastGroup(m_groupAddress);  //退出组播m_udpSocket->abort();       //解除绑定ui->plainTextEdit->appendPlainText("[用户:" + getLocalIp() +"]  退出组播成功");ui->actStart->setEnabled(true);ui->actStop->setEnabled(false);
}void ExMulticast::on_actClear_triggered()
{ui->plainTextEdit->clear();
}void ExMulticast::on_actQuit_triggered()
{close();
}void ExMulticast::onSocketStateChange(QAbstractSocket::SocketState socketState)
{switch (socketState) {case QAbstractSocket::UnconnectedState:m_labSocketState->setText("socket状态:UnconnectedState");break;case QAbstractSocket::HostLookupState:m_labSocketState->setText("socket状态:HostLookupState");break;case QAbstractSocket::ConnectingState:m_labSocketState->setText("socket状态:ConnectingState");break;case QAbstractSocket::ConnectedState:m_labSocketState->setText("socket状态:ConnectedState");break;case QAbstractSocket::BoundState:m_labSocketState->setText("socket状态:BoundState");break;case QAbstractSocket::ClosingState:m_labSocketState->setText("socket状态:ClosingState");break;case QAbstractSocket::ListeningState:m_labSocketState->setText("socket状态:ListeningState");break;default:m_labSocketState->setText("socket状态:其他未知状态...");break;}
}void ExMulticast::onSocketReadyRead()
{while (m_udpSocket->hasPendingDatagrams()) {QByteArray datagram;datagram.resize(m_udpSocket->pendingDatagramSize());QHostAddress peerAddr;quint16 peerPort;m_udpSocket->readDatagram(datagram.data(), datagram.size(), &peerAddr, &peerPort);  //读取数据包,消息+来自的Ip和portQString str = datagram.data();QString peer = QString("[From: %1  %2] %3").arg(peerAddr.toString()).arg(QString::number(peerPort)).arg(str);ui->plainTextEdit->appendPlainText(peer);}
}QString ExMulticast::getLocalIp()
{QString hostName = QHostInfo::localHostName();QHostInfo hostInfo = QHostInfo::fromName(hostName);QString Ip = "";if (hostInfo.addresses().isEmpty())return 0;foreach (QHostAddress addr, hostInfo.addresses()) {if (addr.protocol() == QAbstractSocket::IPv4Protocol) {Ip = addr.toString();break;}}return Ip;
}void ExMulticast::on_btnSend_clicked()
{quint16 port = ui->spinBoxBind->value();QString  msg = ui->lineEdit->text();QByteArray  datagram = msg.toUtf8();m_udpSocket->writeDatagram(datagram, m_groupAddress, port);
//    m_udpSocket->writeDatagram(datagram.data(), datagram.size(), m_groupAddress, port);ui->plainTextEdit->appendPlainText("[multicst] " + msg);ui->lineEdit->clear();ui->lineEdit->setFocus();
}

界面实现如下入图

Qt中使用QUdpSocket类实现UDP通信相关推荐

  1. 在C#中使用SerialPort类实现串口通信 遇到多线程问题

    在C#中使用SerialPort类实现串口通信 遇到多线程问题 在C#中使用SerialPort类实现串口通信. 2009年11月01日 星期日 10:03 在.NET work 2.0中提供了Ser ...

  2. Qt中多线程与界面组件的通信

    文章目录 1 多线程与界面组件的通信 1.1 通过信号与槽实现多线程与界面组件的通信 1.2 通过自定义事件实现多线程与界面组件的通信 1 多线程与界面组件的通信 有趣的问题: 是否可以在子线程中创建 ...

  3. C++中的stack类、QT中的QStack类

    C++中的stack 实现一种先进后出的数据结构,是一个模板类. 头文件 #include<stack> 用法(以int型为例): stack <int> s; //定义一个i ...

  4. C++中的queue类、QT中的QQueue类

    C++中的queue 实现一种先进先出的数据结构,是一个模板类 头文件 #include<queue> 用法(以int型为例): queue<int> Q; //定义一个int ...

  5. 在C#中使用SerialPort类实现串口通信

    在.NET Framework 2.0中提供了SerialPort类,该类主要实现串口数据通信等.本文章将本人在学习过程中从网络上搜集到的相关信息写出来供大家参考. 下面主要介绍该类的主要属性(表1) ...

  6. Qt中一个.cpp类的函数调用另一个.cpp中类的函数的方法

    Qt 下面经常会用到一个函数调用的时候会调用到另一个函数,如果这两个函数在两个.cpp文件中,那么应该怎么调用呢? 假设在B类点击一个按钮会调用A类的一个函数,实际上只需要: 一. 在B的构造函数中c ...

  7. Qt中的QFileDialog类的几个示范代码

    From: http://www.hedabbs.cn/thread-102600-1-1.html void Dialog::setExistingDirectory() {  //选择一个文件夹; ...

  8. Qt、C++小票打印----端口(com)、并口(lpt)----并介绍了qt中qwebview的使用及js通信

    记录一下自己做过的东西,希望对大家可以有帮助. 一.介绍 端口和并口打印,都是读写设备的操作,类似于linux中一切设备皆文件的 赶脚. (1)  利用纯qt实现端口读写,有一个相关的类库可以使用,事 ...

  9. Qt中利用QTime类来控制时间,这里简单介绍一下QTime的成员函数的用法:

    QTime::QTime() 默认构造函数,构造一个时,分,秒都为0的时间,如00:00:00.000(午夜) QTime::QTime(int h, int m, int s=0, int ms = ...

最新文章

  1. 我的超级大魔王Cookie
  2. debian 9/ ubuntu 添加swap分区
  3. java hibernate sql,Java Hibernate中使用SQL 而不使用HQL
  4. Luogu P1031 均分纸牌(贪心)
  5. Python中int(input(请输入一个数))报错:ValueError: invalid literal for int() with base 10: '2.7'
  6. 最近新明白的SQL小知识
  7. NFinal ajax
  8. 【优化算法】符号LMS算法【含Matlab源码 697期】
  9. 上海仰邦BX-5K1,BX-5K2系列板卡加入定时开关指令后死机现象及解决方案
  10. python领域驱动_DDD领域驱动设计学习初探
  11. matlab分离实部虚部,MATLAB/simulink中,想让电流表测出的值为复数形式,有实部虚部,将powergui设置为phasors仿真,总是出错...
  12. 程序员怎样才能写出一篇好的博客或者技术文章
  13. 【内网安全】——数据库提权姿势
  14. 好家伙!上天入地混血儿料箱机器人
  15. 网络其他计算机无法访问,win7局域网别人无法访问我的电脑是为什么_win7其他电脑无法访问我的电脑如何修复-win7之家...
  16. 今日分享-自定义返回按钮(与系统按钮位置一致)
  17. 应用交付工程师应该具备的关键能力(售前篇)
  18. 王俊杰:苏宁AI在智慧零售上的应用
  19. WD西部数据硬盘数据丢失了免费恢复方法
  20. 【深度学习目标检测】一、基于yolov3的水果商品检测分类

热门文章

  1. mac mysql data 路径_Mac访问MySQL数据库的一些基本操作
  2. 一个很精妙的高精度电压基准电路
  3. CPABE中KEK树实现用户撤销
  4. [解锁新姿势] 分享 7 个优化代码的技巧
  5. Excel 公式 ------ 写下名字, 自动填写今天的日期
  6. 领导当众说“辛苦了”,抢答“谢谢”会翻车,高手都用这3个话术
  7. Hive中URL解析函数
  8. Revit二次开发——读取cad中的文字信息
  9. html字母显示红色,css中字体红色如何实现?
  10. 2. 交易开拓者-TB 公式(一)