![eb36ee1058c12359766a45d75e6e2d2d.png](https://i-blog.csdnimg.cn/blog_migrate/99e88b60387825da2fe8f7b2691dd5ac.png)
点击上方蓝字关注我吧!
本篇文章大概1500字,阅读时间大约5分钟
本文总结了SO_REUSEADDR参数的用途以及调优方法,不妨带着如下4个问题阅读:
1、该参数是用来做什么的?
2、客户端和服务端,在什么场景下需要配置该参数?
3、TCP不在TIME-WAIT态下,可以允许端口复用么?
4、如何查看某个端口是否被进程占用?
众所周知,在TCP四次挥手时,主动关闭方一般是调用close方法触发TCP协议栈发一个【FIN】报文段给对端,此时主动方进入【FIN-WAIT-1】态,当收到对端的【ACK】报文段后,主动关闭方会进入【FIN-WAIT-2】态,当主动关闭方收到对端的【FIN】报文段,并回送【ACK】报文段后,会进入TIME-WAIT态持续等待2MSL才最终CLOSED。针对服务端,假设它重启了,服务端是主动关闭方,按照TCP协议规范,该条连接的TCP状态会在真正断开之前进入TIME-WAIT态(该图画反了):
而被动关闭方只有CLOSE-WAIT和LAST-ACK中间态,不会有长达2MSL时间的等待。如果服务端是异常宕机或者在外部被kill命令杀死,然后在立即重启,那么大概率就会出现
Address already in use的异常。可能要等上几分钟才能再次重启成功,出于运维效率和服务本身的健壮性角度考虑,这是无法接受的。
可以使用nc命令(全称为NetCat)模拟该异常,nc非常实用,是一个简单可靠的网络工具,可通过TCP或UDP协议传输数据。同时它还是一个网络应用Debug分析器,可以根据需要创建各种不同类型的网络连接。如下是Java版本demo:
启动后打印resue address:false
此时用nc命令连接该服务器,然后在外部杀掉该服务端进程,在立即重启服务器就会报异常——Address already in use,将代码修改为serverSocket.setReuseAddress(true),再次重复上面过程就不会出现上述异常了。注意setReuseAddress必须在bind函数调用之前执行,否则无效。之所以出现这种错误,就是因为主动关闭方需要等待2MSL时间才能彻底释放该链接,处于TIME-WAIT的连接占用的资源不会被操作系统内核释放。而服务器一般都是固定端口,并且按照TCP协议规范——一个链接的四元组必须是唯一的,四元组即源端口,源IP,目的端口和目的IP。四个属性里只要有一个不一样就可以,而操作系统本身的限制更苛刻,故立即重启服务器,操作系统默认会阻止新的监听套接字重复绑定该端口,表现就是抛出“地址已经被使用”的异常。
如下,使用Netty编写服务器时,可以在服务端手动配置该参数为生效:
.option(ChannelOption.SO_REUSEADDR, true)
因为默认情况这个值都为false,在TCP协议中即表现为0,表示关闭。而透传到Java中,其配置的API在不同JVM有不同实现,为了保险起见,编写TCP或者HTTP的服务端程序,一定要主动设置这个参数为true。
下面在看一个问题:当服务端作为主动关闭方,进入TIME-WAIT态之前处于FIN-WAIT-2态,只有当对方的FIN报文段发过来,服务端才转移到TIME-WAIT态,假设由于网络状态不佳,导致客户端发的FIN报文段丢包,此时服务器会一直处于FIN-WAIT-2态,此时配置SO_REUSEADDR还管用么?
答案是管用,即根据TCP协议的规范,关闭TCP连接时不一定是处于TIME-WAIT态才允许复用端口,只是常见的例子都是在TIME-WAIT态翻车的。
以上,就是关于该参数的一些总结和梳理,另外在线上出现该异常后,可以使用netstat命令或者lsof命令查看端口占用情况,比如查看22端口被谁占用。
第一种方法:使用netstat -lptn | grep 22,其中-l是查看处于监听状态的Socket,-t是TCP协议,-p进程名称,-n(number)直接显示IP地址,而不是host:
第二种方法:使用lsof,在linux上一切皆文件,TCP Socket连接也是一个fd。因此使用lsof也可以查看,即lsof -n -P -i:22,写起来不如第一种爽快:
其中-n表示不将IP转换为hostname,-P(大写)表示不将port转换为service name,-i:port表示端口号为22的进程。可以看到22端口被进程号为7150的sshd进程监听。
小结:
1、SO_REUSEADDR参数在服务端和客户端都能使用并生效
2、必须将绑定同一个端口的所有的Socket对象的SO_REUSEADDR都打开才能起作用
3、在服务端配置,必须在bind方法之前设置才能生效
4、一般都是客户端主动关闭连接,此时客户端用不到SO_REUSEADDR参数,因为客户端一般使用临时端口,端口重用的概率极低
5、使用netstat -laptn命令结合gerp可以查询端口占用情况
6、TCP协议规定TIME-WAIT会一直持续2MSL(即两倍的TCP报文段最大生存时间),以此来确保旧连接不会对新连接产生影响。而处于TIME-WAIT态的连接资源不会被内核释放,所以作为服务器,尽量不要频繁的主动断开连接,比如可以使用连接池复用技术以减少TIME-WAIT态导致的资源浪费。
END
点亮在看,你最好看
点击此处写留言~
![6793636bee6471138a3da35bc8f46aaa.png](https://i-blog.csdnimg.cn/blog_migrate/87892d09d0ffa9d55b73c1f13b13d037.png)
![1804868bac237cab54defd98277a1862.gif](https://i-blog.csdnimg.cn/blog_migrate/4485cdad847b05f77440d819d24c0d9b.gif)
点