一、前言

以下代码实现了udp组播的接收和发送数据的功能。但是,在多网卡环境下仍然存在部分问题,但也解决了大部分问题。具体的内容在下面叙述。

二、大致步骤

QT += network .pro文件添加network

#include <QUdpSocket> 导入头文件

QUdpSocket* udpSocket = new QUdpSocket; new个对象

udpSocket->setSocketOption(QAbstractSocket::MulticastLoopbackOption,1); 设置Socket为组播模式

udpSocket->bind(QHostAddress::AnyIPv4,目标端口,QUdpSocket::ShareAddress); 绑定目标IP,端口

QUdpSocket::ShareAddress ; 允许其他服务绑定相同 端口 IP

udpSocket->setMulticastInterface(const QNetworkInterface &iface); 解决多网卡问题

udpSocket->joinMulticastGroup(groupaddress); 加入组播

connect(udpSocket,&QUdpSocket::readyRead,this,&TRDP::receiveMessage); 连接接收函数 receiveMessage为自定义接收槽函数

QByteArray datagram;
datagram.resize(udpSocket->pendingDatagramSize()); 预分配容量
udpSocket->readDatagram(datagram.data(),datagram.size(),目标IP地址,目标端口); UDP读取数据 (地址和端口传入类型为指针或&)

udpSocket->writeDatagram(str.data(),str.length(), 目标IP, 目标port); //发送数据 QByteArray str 发送数据

udpSocket->leaveMulticastGroup(groupaddress); //退出组播

三、具体实现

1.加入组播

定义两个全局变量,方便使用
quint16 port; //目标端口
QHostAddress groupaddress; //目标IP
我的ip和端口是通过界面输入的:
void XXX::on_StartBt_clicked()
{
    port = ui->TargetPortBox->text().toUInt();      //获取目标端口号
    QString ip = ui->TargetIP->text();              //获取目标IP地址
    groupaddress = QHostAddress(ip);				//类型转换
    udpSocket->setSocketOption(QAbstractSocket::MulticastLoopbackOption,1);             //设置Socket为组播模式
    bool ok = udpSocket->bind(QHostAddress::AnyIPv4,port,QUdpSocket::ShareAddress);     //绑定目标IP,端口
    QList<QNetworkInterface> list = QNetworkInterface::allInterfaces(); //获取系统里所有的网络接口
    foreach(QNetworkInterface intf, list){ //遍历所有接口
    	//intf.addressEntries()返回此接口拥有的IP地址列表及其相关的网掩码和广播地址。
        foreach(QNetworkAddressEntry entry, intf.addressEntries()){	
            if (entry.broadcast() != QHostAddress::Null && entry.ip() != QHostAddress::LocalHost && entry.ip().protocol() == QAbstractSocket::IPv4Protocol){
                udpSocket->setMulticastInterface(intf);
            }
        }
    }
    if(ok){
        bool isTrue = udpSocket->joinMulticastGroup(groupaddress);                        //绑定成功则加入组播,反之关闭socket
        if(!isTrue) QMessageBox::warning(NULL,"ERROR","UDP加入组播失败");
        //else {
            //_timer.start(10000);
        //}
    }
    else {
        QMessageBox::warning(NULL,"ERROR","UDP IP、端口连接失败");
        udpSocket->close();
    }
}

通过 udpSocket->setMulticastInterface(intf);即使是在多网卡环境下,依然可以收到数据。但是,我发现在有些电脑上,我只有在发送了一条任意数据后才能接收到数据,没找到具体原因。所有我用了一个取巧的方法,在加入组播成功的同时开启一个定时器,当我接收到数据时重新设置定时器。这样在规定时间内能接受到数据定时器就不会触发。

2.接收数据

要想接收到数据,这条信号槽函数必不可少
connect(udpSocket,&QUdpSocket::readyRead,this,&TRDP::receiveMessage);   //连接接收函数
void XXX::receiveMessage()
{
    while(udpSocket->hasPendingDatagrams()){            //等待读取数据
        QByteArray datagram;
        QHostAddress targetAddress = groupaddress;          //从目标IP读数据
        quint16 targerPort = port;                          //从目标端口读取数据
        datagram.resize(udpSocket->pendingDatagramSize());      //预分配容量
        udpSocket->readDatagram(datagram.data(),datagram.size(),&targetAddress,&targerPort);   //UDP读取数据
        //_timer.start(10000);
}

读取数据没什么难得,步骤基本上就是这么个步骤。

3.发送数据
发送数据也没什么难的,将需要发送得数据根据协议进行封包就行,没有协议就更简单。

void XXX::slot_control_send(int count)
{
    QString tem = QString("%1%2%3%4%5%6%7%8%9%10").arg("XX").arg("XX").arg("XX").arg("XX").arg("XX").arg("XX").arg("XXXX").arg("XXXX").arg(count,4,16,QLatin1Char('0')).arg("0000000000000000");
    QByteArray str = data_packet_function(tem);                     //对数据进行TRDP封包
    udpSocket->writeDatagram(str.data(),str.length(), groupaddress, port);        //发送数据
}

四、总结

在多网卡环境下,基本能解决接收不到数据得问题,但也会存在一些问题,问题如下:

1. 会遇到在某些电脑上,存在能正常加入组播,但在发送数据下去得情况下,不会主动接收数据,只有在发送数 据后才会取接收数据,甚至会在一段时间后就停止接收数据了,需要再次发送数据,如此反复。

2. 在某些电脑上,会存在使用抓包软件也抓不到数据得情况,这就导致必然接收不到数据,但是能抓到我发送下去的数据。

.

针对第1个问题:因为我这边基本上只需要接收数据10s内至少会接收到一条数据,是针对数据解析得,而且对数据得完整性并不要求,所以在面对第一个问题时,我可以用一个取巧得办法,通过定时器在一段时间后接收不到数据,就触发发送一条数据,这个办法有很大得局限性;但需要接收到每一条数据时,这个方法就会导致数据遗漏。

2个问题目前并没有找到原因。我用的wireshark抓包,抓不到指定端口、IP地址的数据的数据,但能抓到我自己发送下去的数据,我猜测可能是电脑中的某些杀毒软件或防火墙将数据拦截了。