QUdpSocket

前言

我们经常使用 “ping” 命令来测试两台主机之间 TCP/IP 通信是否正常, 其实 “ping” 命令的原理就是向对方主机发送 UDP 数据包,然后对方主机确认收到数据包, 如果数据包是否到达的消息及时反馈回来,那么网络就是通的。

准备工作

首先,要使用 Qt 的网络模块需要在 pro 中加上 network(如果是 VS IDE 就在模块选择里勾选上 network):

QT += network

引入相关类的头文件:

#include <QUdpSocket>
#include <QHostAddress>
#include <QNetworkDatagram>

Qt UDP 的操作流程:
在这里插入图片描述

QUdpSocket的接口

QUdpSocket 是 QAbstractSocket 的子类,用于发送和接收 UDP 数据报。

可以使用 bind() 显式的绑定地址和端口。参数中的地址可以使用 QHostAddress::Any 绑定任意地址,IPv4 等效于 “0.0.0.0” , IPv6 等效于 “::”;而 BindMode 一般可以设置 ShareAddress(允许其他服务绑定到相同的地址和端口) 和 DontShareAddress(不允许其他服务重新绑定),Windows 上默认等效于 ShareAddress。

bool QAbstractSocket::bind(const QHostAddress &address, quint16 port = 0, QAbstractSocket::BindMode mode = DefaultForPlatform)
bool QAbstractSocket::bind(quint16 port = 0, QAbstractSocket::BindMode mode = DefaultForPlatform)

绑定后,只要 UDP 数据报到达指定的地址和端口,就会触发 readyRead() 信号,此时可在槽函数中读取数据:

void QIODevice::readyRead()

可通过 hasPendingDatagrams() 判断是否有可读数据,通过 pendingDatagramSize() 判断数据长度。

对于数据报的读写依然是使用 read/write 相关接口:

qint64 QUdpSocket::readDatagram(char *data, qint64 maxSize, QHostAddress *address = nullptr, quint16 *port = nullptr)
QNetworkDatagram QUdpSocket::receiveDatagram(qint64 maxSize = -1)
qint64 QUdpSocket::writeDatagram(const char *data, qint64 size, const QHostAddress &address, quint16 port)
qint64 QUdpSocket::writeDatagram(const QNetworkDatagram &datagram)
qint64 QUdpSocket::writeDatagram(const QByteArray &datagram, const QHostAddress &host, quint16 port)

对于单播,可以直接指定目标地址和端口发送:

const QString address_text = "127.0.0.1";
const QHostAddress address = QHostAddress(address_text);
const unsigned short port = 12345;
udpSocket->writeDatagram(QNetworkDatagram(send_data,address,port));

对于广播,需要发送到广播地址 “255.255.255.255”,即使用 QHostAddress::Broadcast:

udpSocket->writeDatagram(QNetworkDatagram(send_data,QHostAddress::Broadcast,port));

对于组播,需要发送到指定的组播地址,不过要对方加入了这个组播(使用 joinMulticastGroup 和 leaveMulticastGroup 加入/退出组播):

//组播ip必须是D类ip
//D类IP段 224.0.0.0 到 239.255.255.255
//且组播地址不能是224.0.0.1
udpSocket->bind(QHostAddress::AnyIPv4,port); //根据Qt示例,组播的话IPv4和v6分开的
udpSocket->joinMulticastGroup(address); //QHostAddress("224.0.0.2")
 
udpSocket->writeDatagram(QNetworkDatagram(send_data,address,port)); //QHostAddress("224.0.0.2")

操作完之后,调用相关接口关闭和释放:

void QAbstractSocket::disconnectFromHost()
void QAbstractSocket::close()
void QAbstractSocket::abort()

其中, abort 调用了 close, close 调用了 disconnectFromHost。 abort 立即关闭套接字,并丢弃写缓冲区中的所有待处理数据。close 关闭套接字的 IO,以及套接字的连接。

QUdpSocket对象实例化

QUdpSocket* m_udpSocket;
m_udpSocket = new QUdpSocket(this);

设置套接字选项

void setSocketOption(QAbstractSocket::SocketOption option, const QVariant &value)
//Multicast路由层次,1表示只在同一局域网内
//组播TTL: 生存时间,每跨1个路由会减1,多播无法跨过大多数路由所以为1
//默认值是1,表示数据包只能在本地的子网中传送。
m_udpSocket->setSocketOption(QAbstractSocket::MulticastTtlOption, 1);

QAbstractSocket::MulticastTtlOption:设置多播,即将此设置为整数可以设置IP_MULTICAST_TTL(对于多播数据报为TTL)套接字选项。后面的value就是那个整数,即为TTL值,范围为0~255之间的任何值。

QAbstractSocket::MulticastLoopbackOption:将value设置为1以启用IP_MULTICAST_LOOP(组播回环)套接字选项。即IP_MULTICAST_LOOP用于控制数据是否回送到本地的回环接口,0则禁用。

UDP信号触发
每当QAbstractSocket的状态更改时,都会发出此信号。 socketState参数是新状态。

void QAbstractSocket::stateChanged(QAbstractSocket::SocketState socketState)
connect(m_udpSocket, SIGNAL(stateChanged(QAbstractSocket::SocketState)), this, SLOT(onSocketStateChange(QAbstractSocket::SocketState)));

UDP通信的时候通过stateChanged信号来触发,通过对应的槽函数来触发。

QAbstractSocket::SocketState:套接字的连接状态
UnconnectedState:表示套接字没有连接,对应的默认值是0
ConnectingState:套接字正在执行主机名查找,对应的值是1
ConnectingState:套接字已开始建立连接。对应的值是2
ConnectedState:连接处于建立状态,对应值是3
BoundState:套接字绑定到地址和端口。对应值是4
ListeningState:监听状态,仅内部使用,对应值是5
ClosingState:套接字即将关闭(数据可能仍在等待写入)。对应值是6

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;
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

阳光开朗男孩

你的鼓励是我最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值