Java TCP/IP协议的Socket如何设置端口复用?
1). 使用new Socket(ServerAddress, ServerPort, ClientAddress, ClientPort);绑定端口建立连接后,
Client端发送“end”信息至Server端,Client端的Socket被close();掉。
2). Server端接收到“end”信息后ServerSocket被close();掉。
3). 接着我使用Client端重新连接Server端,结果报错:
java.net.BindException: bind failed: EADDRINUSE (Address already in use)
貌似是Client端的Socket被close掉后,端口并没有被及时释放,但是过一段时间又可以建立连接。
现在我想要解决的问题是,如何在Client端的Socket被close掉后能够及时释放端口供我使用呢? 展开
你的其中一端的连接没有被及时释放掉的原因是:你没有顺利地进行TCP连接关闭的流程。最近我就因为这个问题头疼了好久,现在终于找到真正的原因和解决办法了!关键点是:在调用close之前先发送一次数据(例如,out.write(0);)。接下来我用通俗的语言来阐述原因。
如果你两端的程序都是在传送完数据后直接调用Socket的close方法断开连接的话,这就会有一个调用close方法谁先谁后的问题,先调用者是主动关闭TCP连接的那一方,而后调用者就是被动关闭TCP连接的那一方。
关闭TCP连接的过程中,双方总共有3个报文需要发送,主动关闭方只需发送1个报文,就是断开请求的报文,被动关闭方要发送2个报文,分别是 回应主动关闭方发来的断开请求报文的第一次确认报文 和 第二次确认报文,第二次的确认报文用来确认已经没有数据需要发送了。
主动关闭方发送了断开请求报文后,就进入了等待被动关闭方发送确认报文的状态,一般情况下,被动关闭方马上就会发送第一次确认报文。主动关闭方收到第一次确认报文后,就会等被动关闭方的第二次确认报文,直到超时,套接字资源被操作系统回收(P.S. 虽然书上不是这么说的,但是实际的OS就是这么处理的,原因下面再说)。此时如果被动关闭方发送了第二次确认报文,整个TCP连接关闭流程就顺利结束了,所有资源就会被OS回收。
重点来了,问题在于,作为被动关闭方的程序中,在主动关闭方等待被动关闭方发送第二次确认报文时,就算执行了Socket的close也不会发送第二次确认报文,只有向主动关闭方发送数据(任意)后,被动关闭方才会发送第二次确认报文,整个流程才能顺利进行,TCP连接的资源才会被释放,下次才能重复使用同个套接字。
我自己实验出来的结果是:在两端的代码中(只有一端发送一端接收),都只使用
out.write(0);
me.close();
进行TCP连接的断开,out是Socket的OutputStream,me是已连接的Socket对象。结果,无论重复多少次运行,都能不间断地顺利运行,每次运行结束后,用资源监视器看TCP连接情况,都看到所使用的连接资源都已经被释放,并没有等待2MSL时间后才被释放,所以实际的OS的处理跟理论上的不一样。所以你的程序要等一段时间后才能再次使用同个端口并不是因为存在2MSL的等待时间,而是因为TCP连接关闭流程没有顺利进行,但是所使用的进程已经退出,OS就会自动帮你回收资源,不过需要等超时后才处理。(P.S. 我测试的OS是Windows)
如果两端的程序断开TCP连接时都是直接开始断开的过程而没有延时的话,谁是主动关闭方就不确定,两端的程序中的断开TCP连接的代码就得有发送数据的部分,如果其中有一方延时了,那延时的一方就有很大的概率是被动关闭方,另一方在调用close前就不需要发送数据了。
还有,就算TCP连接关闭的流程顺利进行,但是,下次使用同个套接字(两端套接字跟之前的相同)前得延时一下,几百毫秒就够了(具体自己调整),因为OS回收套接字资源是需要时间的,关闭TCP连接后就立马创建两端套接字跟所关闭的连接的两端套接字相同的连接也有可能因为仍然被占用而报错。
给你点+32赞!两年前的问题,你还很认真的回答并写了这么多字,有点小感动。谢谢啦!
谢谢!那时候在网上到处都找不到满意的回答,所以我想把我那时候悟出来的方法和道理都记录下来让别人知道真相,于是就顺便写在相关问题的回答上了,写的时候还是很激动的。。。我是在刚刚实验成功后写的,就当作是我的实验体会吧!
2023-07-25 广告
什么意思,怎么释放?我这个没用到web,客户端是android应用。
做一个假设的例程 可能要套用代码库 我记得有一个是可以直接把服务端送到客户入口的代码 你可以去看一下