例子
服务端Ctrl + C在服务器关闭连接,重启服务器端程序可能会重启失败,报错信息为:bind failed: Address already in use。
原因:TIME_WAIT
通过服务器端发起的关闭连接操作,引起了一个已有的 TCP 连接处于 TME_WAIT 状态,正是这个 TIME_WAIT 的连接,使得服务器重启时,继续绑定在 127.0.0.1 地址和 9527 端口上的操作,返回了 Address already in use 的错误。
解决:重用套接字选项
一个TCP通过四元组来唯一确定,如果每次客户端使用的本地端口都不同,就不会和已有的四元组冲突,也就不会有TIME_WAIT新旧连接化身冲突的问题。
四元组相同时的优化:
●新连接SYN告知的初始序列号,一定比TIME_WAIT老连接的末序列号大,可以通过序列号区别新老连接。
●SO_REUSEADDR套接字选项,允许一个TIME__WAIT的TCP连接忽略掉旧连接,重新被新连接使用。还有一个作用,如果本机服务器有多个地址,可以在不同地址的相同端口提供服务。
int on = 1;
setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
准则
服务端程序,都应该设置SO_REUSEADDR套接字选项,以便服务端程序可以在极短时间内复用同一个端口启动。可以快速重启
SO_REUSEADDR 套接字选项还有一个作用,那就是本机服务器如果有多个地址,可以在不同地址上使用相同的端口提供服务。
但是TCP不允许在相同的地址和端口绑定不同的服务器,即使设置了SO_REUSEADDR也不行
tcp_tw_reuse 和 SO_REUSEADDR
●tcp_tw_reuse 是内核选项,主要用在连接的发起方。TIME_WAIT 状态的连接创建时间超过 1 秒后,新的连接才可以被复用,注意,这里是连接的发起方(发起方才能进入TIME_WAIT);
tcp_tw_reuse是为了缩短time_wait的时间,避免出现大量的time_wait链接而占用系统资源,解决的是accept后的问题;
●SO_REUSEADDR 是用户态的选项,SO_REUSEADDR 选项用来告诉操作系统内核,如果端口已被占用,但是 TCP 连接状态位于 TIME_WAIT ,可以重用端口。如果端口忙,而 TCP 处于其他状态,重用端口时依旧得到“Address already in use”的错误信息。注意,这里一般都是连接的服务方。
SO_REUSEADDR是为了解决time_wait状态带来的端口占用问题,以及支持同一个port对应多个ip,解决的是bind时的问题。