TCP套接字能否绑定至已被使用的本地地址?
为何已被使用的本地TCP地址可重复绑定至新套接字?
问题代码
int fd1 = socket(...); bind(fd1, "0.0.0.0:0"); connect(fd1, addr1); // ------------ EINPROGRESS addr = getsockname(fd1); int fd2 = socket(...); bind(fd2, addr); connect(fd1, addr2); // ------------- EINPROGRESS send(fd1, message); // ---------------> OK
所有调用均执行成功,无法理解为何仍在使用的本地地址可以绑定至fd2;这是否被允许?
strace 输出
socket(AF_INET, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK, IPPROTO_TCP) = 27 setsockopt(27, SOL_SOCKET, SO_REUSEPORT, [1], 4) = 0 setsockopt(27, SOL_SOCKET, SO_REUSEADDR, [1], 4) = 0 bind(27, {sa_family=AF_INET, sin_port=htons(0), sin_addr=inet_addr("0.0.0.0")}, 16) = 0 connect(27, {sa_family=AF_INET, sin_port=htons(21116), sin_addr=inet_addr("192.168.3.47")}, 16) = -1 EINPROGRESS --------------------------- 1 getsockopt(27, SOL_SOCKET, SO_ERROR, [0], [4]) = 0 setsockopt(27, SOL_TCP, TCP_NODELAY, [1], 4) = 0 getsockname(27, {sa_family=AF_INET, sin_port=htons(41473), sin_addr=inet_addr("192.168.3.47")}, [128->16]) = 0 ---------------------------- 2 socket(AF_INET, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK, IPPROTO_TCP) = 28 setsockopt(28, SOL_SOCKET, SO_REUSEPORT, [1], 4) = 0 setsockopt(28, SOL_SOCKET, SO_REUSEADDR, [1], 4) = 0 bind(28, {sa_family=AF_INET, sin_port=htons(41473), sin_addr=inet_addr("192.168.3.47")}, 16) = 0 ----------------------------------------- 3 connect(28, {sa_family=AF_INET, sin_port=htons(21117), sin_addr=inet_addr("192.168.3.47")}, 16) = -1 EINPROGRESS -------------------------- 4 getsockopt(28, SOL_SOCKET, SO_ERROR, [0], [4]) = 0 setsockopt(28, SOL_TCP, TCP_NODELAY, [1], 4) = 0 getsockname(28, {sa_family=AF_INET, sin_port=htons(41473), sin_addr=inet_addr("192.168.3.47")}, [128->16]) = 0 epoll_ctl(12, EPOLL_CTL_DEL, 28, NULL) = 0 fcntl(28, F_GETFD) = 0x1 (flags FD_CLOEXEC) close(28) = 0 sendto(27, "\304R/\n\v\236\332\346wXif\311_\375\1\22\t426463026\32\f192"..., 50, MSG_NOSIGNAL, NULL, 0) = 50 ------------------------------- 5
原因解析
这种行为是被允许的,核心原因在于TCP套接字的地址复用规则,以及代码中设置的SO_REUSEADDR和SO_REUSEPORT选项:
SO_REUSEADDR的作用:该选项允许绑定到处于TIME_WAIT状态的地址,更关键的是,对于主动发起连接的套接字(调用connect而非listen的套接字),只要新套接字的连接目标与已有套接字不同,就可以复用同一个本地端口。此场景中,fd1连接的是端口21116,fd2连接的是端口21117,两个连接的四元组(本地IP:本地端口, 远端IP:远端端口)完全不同,TCP协议栈不会混淆这两个连接。SO_REUSEPORT的辅助作用:该选项允许多个套接字绑定到完全相同的本地地址和端口,前提是所有绑定的套接字都设置了这个选项。代码中fd1和fd2都开启了该选项,进一步消除了绑定限制。- 连接状态的影响:fd1的
connect返回EINPROGRESS说明连接仍在建立过程中,但getsockname已能获取到分配的本地端口(41473)。此时该端口并未被完全独占——TCP允许同一端口发起多个不同目标的主动连接,只要四元组唯一即可。
总结来说,只要两个套接字的四元组不冲突,且设置了合适的复用选项,就可以复用同一个本地端口进行主动连接,这是TCP协议设计中允许的行为,目的是提高端口资源的利用率。
内容的提问来源于stack exchange,提问作者user2256177
相关产品推荐
相关产品推荐

