如果您不愿意更改可用的临时端口数量(如 David 所建议的那样),或者您需要的连接数超过了理论最大值,还有其他两种方法可以减少正在使用的端口数量。但是,它们在不同程度上违反了 TCP 标准,因此应谨慎使用。
第一种是开启SO_LINGER,超时时间为零,强制TCP堆栈发送RST数据包并刷新连接状态。但是有一个微妙之处:您应该在close 之前调用套接字文件描述符上的shutdown,这样您就有机会在RST 数据包之前发送FIN 数据包。所以代码看起来像:
shutdown(fd, SHUT_RDWR);
struct linger linger;
linger.l_onoff = 1;
linger.l_linger = 0;
// todo: test for error
setsockopt(fd, SOL_SOCKET, SO_LINGER,
(char *) &linger, sizeof(linger));
close(fd);
如果FIN 数据包与RST 数据包重新排序,服务器应该只会看到过早的连接重置。
请参阅TCP option SO_LINGER (zero) - when it's required 了解更多详情。 (在实验上,setsockopt 的设置位置似乎并不重要。)
第二个是使用SO_REUSEADDR 和显式bind(即使您是客户端),这将允许Linux 在您运行时重用临时端口,然后再等待。请注意,您必须将bind 与INADDR_ANY 和端口0 一起使用,否则SO_REUSEADDR 将不受尊重。您的代码将类似于:
int opts = 1;
// todo: test for error
setsockopt(fd, SOL_SOCKET, SO_REUSEADDR,
(char *) &opts, sizeof(int));
struct sockaddr_in listen_addr;
listen_addr.sin_family = AF_INET;
listen_addr.sin_port = 0;
listen_addr.sin_addr.s_addr = INADDR_ANY;
// todo: test for error
bind(fd, (struct sockaddr *) &listen_addr, sizeof(listen_addr));
// todo: test for addr
// saddr is the struct sockaddr_in you're connecting to
connect(fd, (struct sockaddr *) &saddr, sizeof(saddr));
这个选项不太好,因为您仍然会根据netstat -an | grep -e tcp -e udp | wc -l 使 TCP 连接的内部内核数据结构饱和。但是,在这种情况发生之前,您不会开始重用端口。