QUdpSocket
使用上存在几个注意事项,当其结果不符合预期时看看这里有没有记录。
QUdpSocket::readyRead()
记录一个关于 信号的 bug ,有数据到达但此信号不再 emit :官方相似 bug 描述
其内部实现依赖 hasPendingData
标志位判断是否发出信号(并调用关联的槽函数) ,但此标志位容易出错, Qt 将此标志位的复位操作交给了用户:
Note: An incoming datagram should be read when you receive the
readyRead()
signal, otherwise this signal will not be emitted for the next datagram.
1 | // 摘自 Qt5.15.15 的 qabstractsocket.cpp +740 |
通过以上代码实现,可以推断出以下时序:
内部实现响应了 socket 的可读事件,执行了
canReadNotification()
未执行
QUdpSocket::readDatagram()
或QUdpSocket::receiveDatagram()
修改标志位内部实现响应了 socket 的可读事件,执行了
canReadNotification()
不调用
QUdpSocket::readDatagram()
或QUdpSocket::receiveDatagram()
就再也不会emitReadyRead()
如何复现问题呢?
新建的 QUdpSocket
对象如果不关联 readyRead()
槽函数,是不会主动接收数据的,也就不会执行 canReadNotification()
- 给
readyRead()
信号关联槽函数,且数据到达时才会执行canReadNotification()
- 槽函数中不调用
receiveDatagram()
等相关接口;或某分支中未执行相关接口;或disconnect
槽函数 - 数据到达,但不再
emitReadyRead()
。
针对此问题的 workaround 围绕 receiveDatagram()
展开:
- 槽函数中要保证所有的分支都会执行
receiveDatagram()
函数 - 如果中途删除槽函数,重新关联槽函数时调用
receiveDatagram()
保证状态复位
datagram without payload
在 hasPendingDatagrams()
内部实现中存在以下注释:UDP 有长度为零的报文 (udp datagram without payload ),所以额外提供了此接口,在收到零长度报文时也返回 true
。
1 | /*! |
参考 Qt 手册 qint64 QUdpSocket::pendingDatagramSize() const
,没有报文时返回 -1 ,而非 0 。
Returns the size of the first pending UDP datagram. If there is no datagram available, this function returns -1.
bad-checksum
需要强调的是,以下 Qt 手册中例子也是存在问题的:hasPendingDatagrams()
返回值可能是 false
。
1 | void Server::initSocket() |
生产环境下遇到过 bad checksum 报文触发 readyRead()
信号执行槽函数,但 hasPendingDatagrams()
不成立,未能执行 receiveDatagram()
函数,后续收到新报文不再触发 readyRead()
信号。
因为数据是收到了的,只是不再触发 readyRead()
信号,所以当时的解决方案是改用定时器轮询。
todo
一般来说,bad checksum 会被内核过滤并丢弃。如何监控、查看此过程?什么情况下会不再过滤/过滤失败?
POSIX socket()
using SOCK_DGRAM
会过滤掉校验和错误的包,难道 QUdpSocket 不是基于 SOCK_DGRAM
,而是封装的 SOCK_RAW
吗 ?
‘SOCK_RAW’ option in ‘socket’ system call
socket SO_NO_CHECK
除了在生产环境录取的异常报文,如何模拟 bad checksum 报文呢?
workaround
1 | Q_ASSERT(nullptr != socket); |
QNetworkDatagram
数据报 QNetworkDatagram
接口有坑
1 | QNetworkDatagram datagram(getPayload(), destinationAddress, destinationPort); |
抓包过滤字节
tcpdump 和 wireshark 过滤字节的语法存在差异:
1 | tcpdump udp[34:2]=0xa753 |
网卡 offload 特性
查看网卡的 offload 特性 ethtool -k enaphyt4i0
接收网络报文关闭校验 ethtool -K enaphyt4i0 rx off
How to disable checksums on ethernet card in Windows 10?
网卡属性 - 配置 - 高级 - UDP 校验和分载传输(IPv4)