QUdpSocket-Qt使用Udp通讯实现服务端和客户端
版权声明:若无来源注明,Techie亮博客文章均为原创。 转载请以链接形式标明本文标题和地址:
本文标题:QUdpSocket-Qt使用Udp通讯实现服务端和客户端 本文地址:http://techieliang.com/2017/12/532/
摘自:https://www.cnblogs.com/techiel/p/7978049.html
https://blog.****.net/we1583004we/article/details/79188305
https://www.cnblogs.com/wurenzhong/p/8030220.html
文章目录
1. QNetworkDatagram
qt网络报文,可用其建立一个通讯内容包括目标ip、端口号、数据内容。同时接收到的信息也未此类型,可以访问接收数据的长度、发送者的ip及端口等信息
详情请见帮助
QUdpSocket
有Qt提供的udp通讯的类,详细介绍请见官方文档
2. 客户端
注意pro文件要包含QT += network
- #include <QHostAddress>
- #include <QUdpSocket>
- QUdpSocket *m_socket=new QUdpSocket;
- m_socket.writeDatagram(msg, QHostAddress::localhost, 8000);
qudpsocket对于发送数据报文提供了三个重载函数:
- qint64 writeDatagram(const char *data, qint64 size, const QHostAddress &address, quint16 port)
- qint64 writeDatagram(const QNetworkDatagram &datagram)
- qint64 writeDatagram(const QByteArray &datagram, const QHostAddress &host, quint16 port)
上面的范例使用的是第三种类型,也可以自行建立QNetworkDatagram对象,修改好内容后直接调用writeDatagram
3. 服务端
服务端与客户端类似,需要先绑定端口“bind”,此函数在QAbstractSocket类中提供
- bool bind(const QHostAddress &address, quint16 port = 0, BindMode mode = DefaultForPlatform)
- bool bind(quint16 port = 0, BindMode mode = DefaultForPlatform)
具体使用方式:
- m_socket = new QUdpSocket;
- m_socket->bind(QHostAddress::localhost, 8000);
- connect(m_socket, SIGNAL(readyRead()), this, SLOT(receive()));
绑定端口以后,需要将此socket的readyread信号与自定义槽函数连接,当服务端收到消息时会触发此信号。
4. 消息收发
由于udp与tcp不同,不需要三次握手建立连接,所以并不会在连接之后记录当前socket。
发送消息在客户端中已经提供示例
接收消息需要使用Qudpsocket提供以下函数:
- qint64 pendingDatagramSize() const
- qint64 readDatagram(char *data, qint64 maxSize, QHostAddress *address = Q_NULLPTR, quint16 *port = Q_NULLPTR)
- QNetworkDatagram receiveDatagram(qint64 maxSize = -1)
当readyRead信号触发时,可直接通过receiveDatagram函数获取消息报文对象,其内存储消息的发送ip、端口、消息内容等所有信息。
也可以通过pendingDatagramSize判断消息长度,然后通过readDatagram获取消息内容。
在readyRead槽函数中可以先调用QUdpSocket::hasPendingDatagrams判断当前是否有需要读取的消息,若有消息再进行pendingDatagramSize获取需要读取的大小
实例:
客户端
udpclient.cpp
-
#include "udpclient.h"
-
#include "ui_udpclient.h"
-
#include <QTextCodec>
-
#include <QDebug>
-
UdpClient::UdpClient(QWidget *parent) :
-
QMainWindow(parent),
-
ui(new Ui::UdpClient)
-
{
-
ui->setupUi(this);
-
//QTextCodec::setCodecForCStrings(QTextCodec::codecForName("UTF-8"));
-
clientudp=new QUdpSocket(this);
-
clientudp->bind(6000,QUdpSocket::ShareAddress);//绑定读数据端口(监听端口),允许其他服务绑定到此端口
-
connect(clientudp,SIGNAL(readyRead()),this,SLOT(recv()));
-
}
-
UdpClient::~UdpClient()
-
{
-
delete ui;
-
}
-
void UdpClient::on_pushButton_clicked()
-
{
-
// QHostAddress serverip;
-
// serverip.setAddress(QString("192.168.0.7"));//这是人为指定ip地址的方法
-
clientudp->writeDatagram(QVariant(ui->textEdit_write->toPlainText()).toByteArray(),QHostAddress::LocalHost,5000);//向5000端口写数据
-
}
-
void UdpClient::recv()
-
{
-
while(clientudp->hasPendingDatagrams())
-
{
-
QByteArray data;
-
data.resize(clientudp->pendingDatagramSize());
-
clientudp->readDatagram(data.data(),data.size());
-
ui->textEdit_show->setText(QVariant(data).toString());
-
}
-
}
-
//注意,监听与发送的端口可以相同也可以不同,它们相对独立,就像有名管道
服务端代码:
udpserver.cpp
-
#include "udpserver.h"
-
#include "ui_udpserver.h"
-
#include <QVariant>
-
UdpServer::UdpServer(QWidget *parent) :
-
QMainWindow(parent),
-
ui(new Ui::UdpServer)
-
{
-
ui->setupUi(this);
-
serverudp=new QUdpSocket(this);
-
serverudp->bind(5000,QUdpSocket::ShareAddress);//绑定读数据端口(监听端口),允许其他服务绑定到此端口
-
connect(serverudp,SIGNAL(readyRead()),this,SLOT(recv()));
-
}
-
UdpServer::~UdpServer()
-
{
-
delete ui;
-
}
-
void UdpServer::recv()
-
{
-
while(serverudp->hasPendingDatagrams())
-
{
-
QByteArray data;
-
data.resize(serverudp->pendingDatagramSize());
-
serverudp->readDatagram(data.data(),data.size());//
-
ui->textEdit_show->clear();
-
ui->textEdit_show->setText(QVariant(data).toString());
-
}
-
}
-
void UdpServer::on_pushButton_clicked()
-
{
-
// QHostAddress serverip;
-
// serverip.setAddress(QString("192.168.0.7"));//这是人为指定ip地址的方法
-
serverudp->writeDatagram(QVariant(ui->textEdit_write->toPlainText()).toByteArray(),QHostAddress::LocalHost,6000);//向6000端口写数据
-
}
-
//注意,监听与发送的端口可以相同也可以不同,它们相对独立,就像有名管道
执行结果:
代码下载链接:
--------------------- 本文来自 小明37 的**** 博客 ,全文地址请点击:https://blog.****.net/we1583004we/article/details/79188305?utm_source=copy
这里说明一下,UDP通信中分为三种通信分别为单播、组播和广播,下面将一一为大家介绍。
同样的我们都需要在工程文件中添加network
QT += core gui network
进行UDP通信需要用到的头文件
#include <QUdpSocket>
这里我们把UDP通信分为两个部分写,一个是发送端,另一个是接收端,而发送端中又分为单播、组播和广播三种形式,下面我们先来看看写发送端的代码程序的步骤:
发送端Udpsend的代码:
1、单播
(1)创建套接字
QUdpSocket mSocket; mSocket = new QUdpSocket();
(2)发送数据到指定的地址和端口号
mSocket->writeDatagram(ui->textEdit->toPlainText().toUtf8(),QHostAddress("192.168.137.1"),6677); 参数:ui->textEdit->toPlainText().toUtf8 要发送的消息 QHostAddress("192.168.137.1") 接收端的ip地址 6677 端口号,要和接收端的一致
2、组播,组播和单播的步骤是一样的,只有ip地址处有区别
组播ip地址范围:224.0.0.0-239.255.255.255 例子:mSocket->writeDatagram(ui->textEdit->toPlainText().toUtf8(),QHostAddress("224.0.0.100"),6677);
3、广播,广播也只有ip地址和单播有区别
广播地址ip:QHostAddress::Broadcast 例子:mSocket->writeDatagram(ui->textEdit->toPlainText().toUtf8(),QHostAddress::Broadcast,6677);
好了,单播、组播和广播的区别应该都了解了,那么我们就来看发送端(udpsend.cpp)的具体代码:
1 #include "udpsend.h" 2 #include "ui_udpsend.h" 3 4 UdpSend:: UdpSend(QWidget *parent) : 5 QMainWindow(parent), 6 ui(new Ui:: UdpSend) 7 { 8 ui->setupUi(this); 9 10 //初始化创建QUdpSocket对象 11 mSocket = new QUdpSocket(); 12 } 13 14 UdpSend::~ UdpSend() 15 { 16 delete ui; 17 } 18 19 void UdpSend::on_sendBt_clicked() 20 { 21 //单播 22 // qint64 len = mSocket->writeDatagram(ui->textEdit->toPlainText().toUtf8(),QHostAddress("192.168.137.1"),6677); 23 24 25 //组播ip地址范围:224.0.0.0-239.255.255.255 26 //qint64 len = mSocket->writeDatagram(ui->textEdit->toPlainText().toUtf8(),QHostAddress("224.0.0.100"),6677); 27 28 //广播 29 qint64 len = mSocket->writeDatagram(ui->textEdit->toPlainText().toUtf8(),QHostAddress::Broadcast,6677); 30 31 32 }
发送端的界面文件我做的很简单,我只做了发送消息框:
看完发送端的代码,我们继续来看接收端的代码(Udprecv)
接收端不管是单播、或者组播还是广播代码都是一样的,下面是写接收端代码的步骤:
1、创建套接字
QUdpSocket mSocket; mSocket = new QUdpSocket();
2、绑定地址和端口号
mSocket->bind(QHostAddress::AnyIPv4,6677); 参数:AnyIPv4 IPv4 6677 端口号,要和发送端的一致
3、等待数据的到来,利用readyRread()
connect(mSocket,SIGNAL(readyRead()),this,SLOT(read_data()));
4、读数据
readDatagram(char * data, qint64 maxSize, QHostAddress * address = 0, quint16 * port = 0) 参数: data:数据 maxSize:数据的大小 address:QHostAddress类型的地址 port:端口号 例子: void UdpRecv::read_data() { QByteArray array; QHostAddress address; quint16 port; array.resize(mSocket->bytesAvailable());//根据可读数据来设置空间大小 mSocket->readDatagram(array.data(),array.size(),&address,&port); //读取数据 ui->listWidget->addItem(array);//显示数据 //发送反馈数据 }
如果是组播的话还涉及到加入组播和退出组播
加入到组播组 joinMulticastGroup 例子:mSocket->joinMulticastGroup(QHostAddress("224.0.0.100")); 退出组播组 leaveMulticastGroup 例子: mSocket->leaveMulticastGroup(QHostAddress("224.0.0.100"));
来看看接收端(Udprecv.cpp)具体实现的代码
1 #include "udprecv.h" 2 #include "ui_udprecv.h" 3 4 UdpRecv::UdpRecv(QWidget *parent) : 5 QMainWindow(parent), 6 ui(new Ui::UdpRecv) 7 { 8 ui->setupUi(this); 9 10 //创建对象 初始化 11 mSocket = new QUdpSocket(); 12 13 //绑定 14 mSocket->bind(QHostAddress::AnyIPv4,6677); 15 //关联读数据信号readyread 16 connect(mSocket,SIGNAL(readyRead()),this,SLOT(read_data())); 17 18 } 19 20 UdpRecv::~UdpRecv() 21 { 22 delete ui; 23 } 24 25 void UdpRecv::read_data() 26 { 27 QByteArray array; 28 QHostAddress address; 29 quint16 port; 30 array.resize(mSocket->bytesAvailable());//根据可读数据来设置空间大小 31 mSocket->readDatagram(array.data(),array.size(),&address,&port); //读取数据 32 ui->listWidget->addItem(array);//显示数据 33 //发送反馈数据 34 35 } 36 37 void UdpRecv::on_checkBox_clicked(bool checked) 38 { 39 if(checked) 40 { 41 //加入组播 42 mSocket->joinMulticastGroup(QHostAddress("224.0.0.100")); 43 } 44 45 else 46 { 47 //退出组播 48 mSocket->leaveMulticastGroup(QHostAddress("224.0.0.100")); 49 50 } 51 52 }
接收端的界面文件我只加了显示接收到的信息和选择是否加入组播的选择按钮
这是发送端和接收分开来写的,此外我也实现了一下发送端和接收端写到同一个文件中
头文件qudpapp.h中的代码
1 #ifndef QUDPAPP_H 2 #define QUDPAPP_H 3 4 #include <QWidget> 5 #include <QUdpSocket> 6 namespace Ui { 7 class QUdpApp; 8 } 9 10 class QUdpApp : public QWidget 11 { 12 Q_OBJECT 13 14 public: 15 explicit QUdpApp(QWidget *parent = 0); 16 ~QUdpApp(); 17 18 private slots: 19 void on_sendSigRb_clicked(); //单播旋转轴 20 void on_sendMulRb_clicked(); //组播选择 21 void on_sendBroadRb_clicked(); //广播选择 22 void on_sendBt_clicked(); //发送按钮 23 24 //=========================================== 25 void on_recvCb_clicked(bool checked); //选择接收 26 void on_recvJoinMulBt_clicked(); //加入组播 27 void on_recvLeaveMulBt_clicked(); //退出组播 28 29 void on_sendMesEdit_cursorPositionChanged();//检测消息框是否有数据 30 void read_data(); 31 32 private: 33 Ui::QUdpApp *ui; 34 QUdpSocket *mSocket; 35 QHostAddress sendaddrees; 36 QString sendPort; 37 38 }; 39 40 #endif // QUDPAPP_H
源文件qudpapp.cpp中的代码
1 #include "qudpapp.h" 2 #include "ui_qudpapp.h" 3 #include <QMessageBox> 4 QUdpApp::QUdpApp(QWidget *parent) : 5 QWidget(parent), 6 ui(new Ui::QUdpApp) 7 { 8 ui->setupUi(this); 9 mSocket = new QUdpSocket();//创建套接字 10 ui->sendBt->setEnabled(false); 11 } 12 13 QUdpApp::~QUdpApp() 14 { 15 delete ui; 16 } 17 18 19 //==========================发送端==================== 20 //单播选择 21 void QUdpApp::on_sendSigRb_clicked() 22 { 23 if(ui->sendPortEdit->text().isEmpty() || ui->sendSigAddrEdit->text().isEmpty()) 24 { 25 QMessageBox::warning(this,"提示","请输入单播ip和端口号"); 26 //ui->sendSigRb->setChecked(false); 27 return; 28 } 29 sendaddrees.setAddress( ui->sendSigAddrEdit->text()); 30 sendPort = ui->sendPortEdit->text(); 31 32 } 33 34 //组播选择 35 void QUdpApp::on_sendMulRb_clicked() 36 { 37 if(ui->sendPortEdit->text().isEmpty() || ui->sendMulAddrEdit->text().isEmpty()) 38 { 39 QMessageBox::warning(this,"提示","请输入组播ip和端口号"); 40 //ui->sendSigRb->setChecked(false); 41 return; 42 } 43 sendaddrees.setAddress( ui->sendMulAddrEdit->text()); 44 sendPort = ui->sendPortEdit->text(); 45 46 } 47 48 //广播选择 49 void QUdpApp::on_sendBroadRb_clicked() 50 { 51 if(ui->sendPortEdit->text().isEmpty() || ui->sendBroadAddrEdit->text().isEmpty()) 52 { 53 QMessageBox::warning(this,"提示","请输入广播ip和端口号"); 54 //ui->sendSigRb->setChecked(false); 55 return; 56 } 57 sendaddrees.setAddress( ui->sendBroadAddrEdit->text()); 58 sendPort = ui->sendPortEdit->text(); 59 60 } 61 62 //发送按钮 63 void QUdpApp::on_sendBt_clicked() 64 { 65 mSocket->writeDatagram(ui->sendMesEdit->toPlainText().toUtf8(),sendaddrees,sendPort.toInt()); 66 67 } 68 69 //检测发送消息对话框中是否有消息 70 void QUdpApp::on_sendMesEdit_cursorPositionChanged() 71 { 72 if(ui->sendMesEdit->toPlainText().isEmpty()) 73 { 74 ui->sendBt->setEnabled(false); 75 } 76 else 77 { 78 ui->sendBt->setEnabled(true); 79 } 80 81 } 82 83 //==========================发送端==================== 84 85 86 //==========================接收端===================== 87 88 //选择接收 89 void QUdpApp::on_recvCb_clicked(bool checked) 90 { 91 if(ui->recvPortEdit->text().isEmpty()) 92 { 93 QMessageBox::warning(this,"提示","请输入端口号"); 94 ui->recvCb->setChecked(false); 95 return; 96 } 97 if(checked) 98 { 99 mSocket->bind(QHostAddress::AnyIPv4,ui->recvPortEdit->text().toInt()); 100 connect(mSocket,SIGNAL(readyRead()),this,SLOT(read_data())); 101 ui->recvPortEdit->setEnabled(false); 102 } 103 else 104 { 105 mSocket->close(); 106 ui->recvPortEdit->setEnabled(true); 107 } 108 } 109 110 //加入组播 111 void QUdpApp::on_recvJoinMulBt_clicked() 112 { 113 if(ui->recvMulAddrEdit->text().isEmpty()) 114 { 115 QMessageBox::warning(this,"提示","请输入组播ip"); 116 return; 117 } 118 if(mSocket->joinMulticastGroup(QHostAddress(ui->recvMulAddrEdit->text()))) //加入组播 119 { 120 ui->recvMulAddr->addItem(ui->recvMulAddrEdit->text()); 121 } 122 else 123 { 124 QMessageBox::warning(this,"提示","加入组播失败,请修改ip后继续加入"); 125 //return; 126 } 127 128 } 129 130 //退出组播 131 void QUdpApp::on_recvLeaveMulBt_clicked() 132 { 133 mSocket->leaveMulticastGroup(QHostAddress(ui->recvMulAddr->currentIndex()));//退出组播地址列表当前的组播 134 ui->recvMulAddr->removeItem(ui->recvMulAddr->currentIndex()); //删除组播地址列表中当前的组播地址 135 } 136 137 void QUdpApp::read_data() 138 { 139 QByteArray array; 140 array.resize(mSocket->bytesAvailable()); //将接收数据的array设置成为要接收数据的大小 141 QHostAddress recvaddress; 142 quint16 port; 143 mSocket->readDatagram(array.data(),array.size(),&recvaddress,&port); //读取数据 144 ui->recvList->addItem(array); 145 146 } 147 148 //==========================接收端=====================
界面文件qudpapp.ui