1 UDP通信概述QUdpSocket类
- UDP协议(用户数据报协议)是不可靠的、轻量的、无连接的协议。与TCP不同UDP通信时,每次发送数据报协议都要指定目标地址和端口。
- QT中通过QUdpSocket类实现UDP通信,它以数据报传输数据,而不是连续的字节流。发送数据报时用writeDatagram函数向指定的地址和端口发送数据报,长度一般小于512Byte。接收数据时,要先用bind函数绑定本机的一个端口,当数据报到来时,readyRead信号被触发,使用readDatagram函数来读取接收到的数据报。
- UDP消息传送有三种模式:单播模式(unicast)、广播模式(boardcast)、组播模式(multicast),TCP只有单播模式。另外UDP的两端是对等的,不像TCP那样分为客户端和服务器。
- 单播指一个UDP客户端发出的数据只发送到另一个指定地址和端口的UDP客户端,是一对一的数据传输。
- 广播指一个UDP客户端发出的数据,在同一网络范围的其它所有UDP客户端都可以收到,只需在数据报中指定接收端地址为QHostAddress::Broadcast,一般广播地址为255.255.255.255
- 组播也称多播,UDP客户端加入到另一个组播IP地址指定的多播组中,成员向组播地址发送的数据报组内成员都可以接收到,类似于QQ群。
- QUdpSocket类的主要接口函数:
bool QAbstractSocket::bind(quint16 port = 0, BindMode mode = DefaultForPlatform);
qint64 QUdpSocket::writeDatagram(const QByteArray &datagram, const QHostAddress &host, quint16 port);
qint64 QUdpSocket::writeDatagram(const char *data, qint64 size, const QHostAddress &address, quint16 port);
bool QUdpSocket::hasPendingDatagrams() const;
qint64 QUdpSocket::pendingDatagramSize() const;
qint64 QUdpSocket::readDatagram(char *data, qint64 maxSize, QHostAddress *address = Q_NULLPTR, quint16 *port = Q_NULLPTR);
bool QUdpSocket::joinMulticastGroup(const QHostAddress &groupAddress);
bool QUdpSocket::leaveMulticastGroup(const QHostAddress &groupAddress);
调用bind函数在本机绑定指定端口port,用于UDP通信。
writeDatagram函数向指定的地址host和端口port发送数据报datagram,返回成功发送的字节数,数据报不要太长,不要超过512Byte。
hasPendingDatagrams函数返回true,当至少有一个数据报需要读取。
pendingDatagramSize函数第一个待读取的数据报的大小Byte。
readDatagram函数用来读取数据报,address 和port指定发送者的地址和端口号,最多读maxSize个Byte,返回成功读取的字节数。
joinMulticastGroup函数使socket加入一个多播组groupAddress。
leaveMulticastGroup函数使socket离开多播组groupAddress。
2 示例
2.1 单播
//udpClient.cpp
QUdpSocket *udpSocket = new QUdpSocket(this);//用于与连接的客户端通讯的QTcpSocket
udpSocket->bind(port);//绑定一个本机的端口,用于接收他人传来的数据报
connect(udpSocket,SIGNAL(stateChanged(QAbstractSocket::SocketState)),
this,SLOT(onSocketStateChange(QAbstractSocket::SocketState)));//指示状态变化
connect(udpSocket,SIGNAL(readyRead()),this,SLOT(onSocketReadyRead()));//数据报到来时readyRead信号触发
void MainWindow::onSocketReadyRead()
{//读取收到的数据报
while(udpSocket->hasPendingDatagrams())//有未读的数据报
{
QByteArray datagram;
datagram.resize(udpSocket->pendingDatagramSize());//将datagram初始化为到来的数据报的大小
QHostAddress peerAddr;
quint16 peerPort;
udpSocket->readDatagram(datagram.data(),datagram.size(),&peerAddr,&peerPort);
QString str=datagram.data();//数据是字符串的话才转化
}
}
void MainWindow::on_btnSend_clicked()
{//发送数据报消息
QString targetIP=ui->comboTargetIP->currentText();
QHostAddress targetAddr(targetIP);//目标IP
quint16 targetPort=ui->spinTargetPort->value();//目标port
QString msg=ui->editMsg->text();//发送的消息内容
QByteArray str=msg.toUtf8();//转化为QByteArray
udpSocket->writeDatagram(str,targetAddr,targetPort); //发出数据报
}
udpSocket->abort(); //解除绑定
2.2 广播
发出广播消息时,writeDatagram函数中指定的地址改为QHostAddress::Broadcast,别的和单播的情形相同。
void MainWindow::on_btnBroadcast_clicked()
{ //发送广播消息
quint16 targetPort=ui->spinTargetPort->value(); //目标端口
QString msg=ui->editMsg->text();
QByteArray str=msg.toUtf8();
udpSocket->writeDatagram(str,QHostAddress::Broadcast,targetPort);
}
2.3 组播
UDP组播是主机间一对一组的通信模式,当多个客户端加入由一个组播地址定义的多播组后,客户端向这个组播地址和端口发送的UDP数据报,组内成员都可以接收到。组播地址是D类IP地址,有特定的地址段,这里不再详细介绍。
239.0.0.0~239.255.255.255为本地管理组播地址范围,家庭办公室局域网测试UDP组播选用这一范围。
QUdpSocket *udpSocket = new QUdpSocket(this);//用于与连接的客户端通讯的QTcpSocket
QHostAddress groupAddress;//组播地址
//Multicast路由层次,1表示只在同一局域网内,默认值是1,表示数据包只能在本地的子网中传送。
//组播TTL: 生存时间,每跨1个路由会减1,多播无法跨过大多数路由所以为1
udpSocket->setSocketOption(QAbstractSocket::MulticastTtlOption,1);
connect(udpSocket,SIGNAL(readyRead()),this,SLOT(onSocketReadyRead()));
//readyRead槽函数,读取数据报
void MainWindow::onSocketReadyRead()
{
while(udpSocket->hasPendingDatagrams())
{
QByteArray datagram;
datagram.resize(udpSocket->pendingDatagramSize());
QHostAddress peerAddr;
quint16 peerPort;
udpSocket->readDatagram(datagram.data(),datagram.size(),&peerAddr,&peerPort);
QString str=datagram.data();
}
}
//加入组播
QString IP=ui->comboIP->currentText();
QHostAddress groupAddress=QHostAddress(IP);//多播组地址
quint16 groupPort=ui->spinPort->value();//端口
if (udpSocket->bind(QHostAddress::AnyIPv4, groupPort, QUdpSocket::ShareAddress))//先绑定端口
udpSocket->joinMulticastGroup(groupAddress); //加入多播组
//发送组播消息
quint16 groupPort=ui->spinPort->value();
QString msg=ui->editMsg->text();
QByteArray datagram=msg.toUtf8();
udpSocket->writeDatagram(datagram.data(),datagram.size(),groupAddress,groupPort);
//退出组播
udpSocket->leaveMulticastGroup(groupAddress);
udpSocket->abort(); //解除绑定