问题
两台机子使用可靠UDP来连接,结果发现接收方收到报文后没有任何反应,即没有反馈发回任何报文回来。
排查
-
通过在接收端(服务端)log信息可以看到,他是在
recvmsg
时,提取控制信息为空,导致无法获取控制消息中的源地址,判断出错,退出连接。 -
通过log中信息判断这是两个IPv6地址连接之间,同时进一步查看两个地址,可以发现这是两个地址实际上IPv4地址,包装成了一个IPv6地址。注意到这点很重要,先前已经注意到一个IPv6地址,当从控制信息提取目的地址时,如果是一个实际为v4的v6地址,需要使用
IP_PKTINFO
类型来判断获取,而不是使用IPV6_PKTINFO
。这是处理v6信息的特殊之处。 -
由于是因为接收端没有接收到控制信息,怀疑是发送端没有把数据送出来,注意到发送端套接字选项
IP_PKTINFO
的设置情况,怀疑是选项没有设置正确。后面调试log证实了这个猜想:由于这个地址实际是一个v4地址,实际要设置的是IP_PKTINFO
,而不是IPV6_PKTINFO
;如果设置当前是个0地址,由于不能确定这个地址实际是属于哪个地址族的,建议v4、v6的pktinfo
都一起设置。套接字代码如下:int on = 1; if (af == AF_INET) { return setsockopt(fd, IPPROTO_IP, IP_PKTINFO, (const void *)&on, sizeof(on)); } #ifdef IPV6_RECVPKTINFO return setsockopt(fd, IPPROTO_IPV6, IPV6_RECVPKTINFO, (const void *)&on, sizeof(on)); #else #if defined(__APPLE__) return setsockopt(fd, IPPROTO_IPV6, IPV6_2292PKTINFO, (const void *)&on, sizeof(on)); #else return setsockopt(fd, IPPROTO_IPV6, IPV6_PKTINFO, (const void *)&on, sizeof(on)); #endif /* __APPLE__ */ #endif/* IPV6_RECVPKTINFO */
-
在修订完发送端并确认设置正常后测试,还是发现接收端收到的消息长度为0。此时从接收端的角度考虑,是否也正确设置了套接字选项?接收端监听时绑定的是0地址,由于设置的是IPv6地址族,所以绑定地址是
::
。发现设置socket选项时,之前判断了它是不是一个v4映射的v6地址,而没有判断它是不是一个0地址。导致此时还是设置了v6的pktinfo
,设置错误会获取不到pktinfo
信息。重新修订成0地址时,同时设置v4和v6的socket选项。至此终于正常接收到控制消息。 -
此问题解决完,我打算重新查看一遍设置发送
pktinfo
的代码,结果竟然没有找到!没有设置,远端也能收到吗?难道内核会自动帮我们打包吗?看到这篇文章也是获取UDP包中控制消息,没有显式设置过控制消息(BTW,我们项目是获取头标识目的地址即ipi_addr
作为local_addr
),同时强调这种方法只能用于UDP传输中(man page的原话是only works for datagram oriented sockets
)。但其他文章有提到在发送数据包前需要设置这个控制信息,更让人不解了我们代码中并没有设置信息,只有选项。 -
进一步查找获悉,这个socket选项主要就是用于设置或获取控制信息使用的。如果不需要特别地变更某个具体内容,那么内核会帮你设置的,只要它看到你的fd设置了这个选项。这个是代码参考。这个是自己设置
IPV6_PKTINFO
的代码参考。 -
收工。