一次 setsockopt 设置引发的问题

最近沉迷于造轮子不可自拔,在实现一个底层网络库时,发现了一个奇怪的 bug:自己写的 server 在 localhost 下一切正常,一旦部署到自己的 VPS 上,所以的请求都无法被 accept

通过 TCPDump 查看握手情况,发现一个诡异的问题,服务器可以收到 SYN,同时当前状态也可以切换为 SYN-RCVD,然后整个连接到此为止,客户端无论如何都收不到服务端回复的 ACK,导致客户端不停的发送 SYN

排查网络路由情况无果后,只能怀疑是代码写错了,开始了愚蠢的二分定位 bug。终于发现了诡异的两行代码:

1
2
int level = family == AF_INET ? IPPROTO_IP : IPPROTO_IPV6;
auto ret = ::setsockopt(fd, level, SO_REUSEADDR, &reuse_addr, sizeof(reuse_addr));

注释掉以后,发现一切正常了起来。仔细观察代码,发现我当时脑子一热,将 setsockopt 写在了 IP 协议级别了,重新改回 SOL_SOCKET 后问题解决。

1
auto ret = ::setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &reuse_addr, sizeof(reuse_addr));

问题解决后,我在想上述错误的代码到底对 IP 层做了哪些设置,导致出现上述奇怪的现象,在 linux 下,将对应的宏定义替换后,得到了如下代码:

1
2
int reuse_addr = 1;
auto ret = ::setsockopt(fd, IPPROTO_IP, IP_TTL, &reuse_addr, sizeof(reuse_addr));

看到代码,心中一阵无语。

1
2
3
IP_TTL (since Linux 1.0)
Set or retrieve the current time-to-live field that is
used in every packet sent from this socket.

也就是说我将 IP 包设置为只能转发一跳,这就解释了为啥 localhost 正常,VPS 环境却无法 ACK 的情况了。

so,记录一下吧~~,造轮子任重而道远啊


by Guweiz