在Linux上“接受”fd上的setsockopt

我对Linux上针对SO_REUSEADDR的setsockopt行为有一个相当奇怪的观察。 在一行中:如果我将sockopt应用到由“监听套接字”上的accept返回的fd,那么套接字选项将反映在监听套接字所持有的端口上。

确定一些代码。

服务器:打开一个套接字,应用SO_REUSEADDR为true。 接受连接,然后将SO_REUSEADDR应用于由accept返回的fd上的fd为false。

#include <stdio.h> #include <sys/socket.h> #include <sys/types.h> #include <arpa/inet.h> #include <string.h> int main(void) { int s, len; int sin_size; int reuse = 1; int ret; struct sockaddr_in my_addr; memset(&my_addr, 0, sizeof(my_addr)); my_addr.sin_family = AF_INET; my_addr.sin_addr.s_addr = inet_addr("127.0.0.1"); my_addr.sin_port = htons(33235); if( (s = socket(AF_INET, SOCK_STREAM, 0)) < 0) { printf("Socket Error\n"); return -1; } setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(int)); if( bind(s, (struct sockaddr*)&my_addr, sizeof(struct sockaddr)) < 0) { printf("Bind Error\n"); return -1; } listen(s, 6); reuse = 0; memset(&my_addr, 0, sizeof(my_addr)); while(1) { ret = accept(s, (struct sockaddr*)&my_addr, &len); if (ret<0) { printf("Accept failed\n"); } else { printf("Accepted a client setting reuse add to 0\n"); setsockopt(ret, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(int)); } } printf("Server exiting\n"); return 0; } 

客户端:客户端连接到服务器,并在此之后不做任何事情,确保服务器套接字保持在TIME_WAIT状态。

 #include <stdio.h> #include <sys/socket.h> #include <sys/types.h> #include <arpa/inet.h> #include <string.h> #include <errno.h> int main(void) { int s, len; int sin_size; struct sockaddr_in my_addr; memset(&my_addr, 0, sizeof(my_addr)); my_addr.sin_family = AF_INET; my_addr.sin_addr.s_addr = inet_addr("127.0.0.1"); my_addr.sin_port = htons(33235); if( (s = socket(AF_INET, SOCK_STREAM, 0)) < 0) { printf("Socket Error\n"); return -1; } if (!connect(s,(struct sockaddr*)&my_addr, sizeof(struct sockaddr))) { printf("Client Connected successfully\n"); } else { printf("%s\n",strerror(errno)); } while(1) sleep(1); return 0; } 

我重现问题的步骤。

  1. 运行服务器。
  2. 连接客户端。
  3. 杀死并重新启动服务器。 服务器失败并出现绑定失败

我在mac os上testing了这个。 绑定并没有失败。 我已经挖掘了所有的Posix规范,没有人说这个代码是未定义的。

题:

能有更多经验的人分享他们对这个问题的理解吗?

一种想法是,SO_REUSEADDR决定是否可以有另一个套接字绑定到同一个地址。 它是任何套接字(侦听或连接)的属性,但是通常通过accept来继承。 在Linux中,它映射到struct sock“sk_reuse”标志。

如果在FD上清除了这个标志,那么“接受”,那么IP /端口对上的这个标志被认为是不可重复使用的。 侦听套接字上的SO_REUSEADDR标志不会改变,但接受的套接字上的标志会影响绑定逻辑。 你可以用getsockopt来检查。

如果你想知道更多,你可以尝试阅读inet_csk_get_port函数: http ://lxr.free-electrons.com/source/net/ipv4/inet_connection_sock.c#L100。 这是实际的“约束”发生的地方。