一,UDP 通信流程
1,创建QUdpSocket
QUdpSocket * udp_p = new QUdpSocket(this);
2,绑定端口并建立打开连接
udp_p->bind(src_port);
udp_p->open(QIODevice::OpenModeFlag::ReadWrite);
bind传入的端口为远程端口
3,建立信号连接
connect(udp_p, &QUdpSocket::readyRead, this, &UdpComm::DoRecv);
connect(udp_p, &QUdpSocket::disconnected, this, &UdpComm::DoOnDisconnected);
connect(udp_p, QOverload<QAbstractSocket::SocketError>::of(&QAbstractSocket::error),
this, &UdpComm::DoError);
其中DoRecv DoOnDisconnected DoError 我们自己定义的槽函数,当收到数据或者断开连接或者发生错误会触发这三个函数的调用,我们可以在这几个函数处理我们的业务逻辑
4,发送数据
QHostAddress addr(des_ip_);
res = udp_p->writeDatagram(data, len, addr, des_port_);
5,接收数据
void UdpComm::DoRecv() {
QByteArray *datagram;
QHostAddress host;
quint16 port = 0;
if(udp_p == nullptr )
{
qDebug() << "DoRecv error udp_p is null" <<endl;
return;
}
if( udp_p->isOpen() == false)
{
qDebug() << "DoRecv error udp_p is not open" <<endl;
return;
}
datagram = new QByteArray();
datagram->resize(static_cast<int>(udp_p->pendingDatagramSize()));
qint64 res;
res = udp_p->readDatagram(datagram->data(),datagram->size(), &host, &port);
if (res < 0) {
emit onError(ErrorRecv, QString::number(res));
} else {
QHostAddress ipv4(host.toIPv4Address());
received_client_ip_ = ipv4.toString();
received_client_port_ = port;
emit onRecv(datagram, received_client_ip_, received_client_port_);
}
delete datagram;
}
void UdpComm::DoError(QAbstractSocket::SocketError se) {
Q_UNUSED(se)
QString error_str = "unknown error";
if(udp_p != nullptr) {
error_str = udp_p->errorString();
}
emit onError(ErrorConnection, error_str);
}
这个方法我们调用 readDatagram方法来接收数据,并且获取发送的IP和地址
二,UDP 广播流程
1,创建QUdpSocket
QUdpSocket * udp_p = new QUdpSocket(this);
2,绑定端口
if(!udp_p->bind(local_ip.isEmpty() ? QHostAddress::Any : QHostAddress(local_ip), des_port_))
{
QString error = QString("Create socket failed! %1").arg(udp_p->errorString());
qDebug() << error <<endl;
udp_p->deleteLater();
}
udp_p->open(QIODevice::OpenModeFlag::ReadWrite);udp_p->open(QIODevice::OpenModeFlag::ReadWrite);
绑定端口需要传入参数,第一个参数是本机设备的IP地址,第二个参数是目标地址的端口。
注意:这里第一个IP一定要填写本机的IP,由于电脑端可能会有多个iP的情况,例如虚拟IP 或者 WiFiIP之类的,对于某些IP,广播之后肯能不成功或者接收不到数据,所以可能需要用户在界面上选择本机IP并且进行多次尝试不同的本机IP进行广播。
下面是获取本机IP的方法
//获取本机IP
QList<QHostAddress> hostAddressList = QNetworkInterface::allAddresses();
foreach (QHostAddress address, hostAddressList)
{
if (!address.isLoopback() && !address.isMulticast() && address.protocol() == QAbstractSocket::IPv4Protocol) {
qDebug() << "本机IP " << address.toString() <<endl;
ui->local_ip->addItem(address.toString());
}
}
我在这里获取到了本机IP之后加入了选择框可供用户选择,尝试发送广播
3,建立信号连接
connect(udp_p, &QUdpSocket::readyRead, this, &UdpComm::DoRecv);
connect(udp_p, &QUdpSocket::disconnected, this, &UdpComm::DoOnDisconnected);
connect(udp_p, QOverload<QAbstractSocket::SocketError>::of(&QAbstractSocket::error),
this, &UdpComm::DoError);
4,发送数据
QHostAddress addr(des_ip_);
res = udp_p->writeDatagram(data, len, addr, des_port_);
注意这里发送的地址一般写死:255.255.255.255 端口就写你要广播的目标端口。当然如果你知道你要广播的地址属于哪个网段,你也可以广播到这个网段.例如:192.168.10.255
5,接收数据
void UdpComm::DoRecv() {
QByteArray *datagram;
QHostAddress host;
quint16 port = 0;
if(udp_p == nullptr )
{
qDebug() << "DoRecv error udp_p is null" <<endl;
return;
}
if( udp_p->isOpen() == false)
{
qDebug() << "DoRecv error udp_p is not open" <<endl;
return;
}
datagram = new QByteArray();
datagram->resize(static_cast<int>(udp_p->pendingDatagramSize()));
qint64 res;
res = udp_p->readDatagram(datagram->data(),datagram->size(), &host, &port);
if (res < 0) {
emit onError(ErrorRecv, QString::number(res));
} else {
QHostAddress ipv4(host.toIPv4Address());
received_client_ip_ = ipv4.toString();
received_client_port_ = port;
emit onRecv(datagram, received_client_ip_, received_client_port_);
}
delete datagram;
}
void UdpComm::DoError(QAbstractSocket::SocketError se) {
Q_UNUSED(se)
QString error_str = "unknown error";
if(udp_p != nullptr) {
error_str = udp_p->errorString();
}
emit onError(ErrorConnection, error_str);
}
三,区别
通过上面可以看出正常UDP通信和发送广播有以下区别
1,广播的时候建立连接的时候需要绑定本机的IP,正常通信不需要
2,广播的时候发送给目标地址是固定的 ,一般是255.255.255.255