17tcp close端口占用 & setReuseAddress 【本地】
https://www.cnblogs.com/diaobiyong/p/9929319.html#p4.4.1 这个链接中4.1,通过客户端绑定端口,出现了客户端第2次起点报端口占用,本文予以实践(mac本地环境):
public class Server { public static final int PORT = 12123; public static final int BUFFER_SIZE = 1024; public static void main(String [] f) throws IOException, InterruptedException { new Server().server(); } //服务端代码 public void server() throws IOException, InterruptedException{ ServerSocket ss = new ServerSocket(PORT); while(true) { Socket s = ss.accept(); s.getOutputStream().write("hello ".getBytes()); s.getOutputStream().flush(); } } }
public class Client { public static final int PORT = 12123; public static final int BUFFER_SIZE = 1024; public static void main(String []f) throws Exception { new Client().client(); } //客户端代码 public void client() throws Exception{ final String s2 = "localhost"; byte[] buffer; Socket s = new Socket(); s.bind(new InetSocketAddress(8888)); s.connect(new InetSocketAddress(s2,PORT)); int i = s.getInputStream().read(buffer = new byte[BUFFER_SIZE]); System.out.println(new String(buffer,0,i)); } }
JoycedeMacBook:netty-test joyce$ netstat -an|grep 8888 长时间
tcp4 0 0 127.0.0.1.12123 127.0.0.1.8888 CLOSE_WAIT
tcp4 0 0 127.0.0.1.8888 127.0.0.1.12123 FIN_WAIT_2
这种情况叫描述符泄漏
看到,这种代码下,服务端居然没发fin包
重启client,报:
Exception in thread "main" java.net.SocketException: Address already in use (connect failed)
at java.net.PlainSocketImpl.socketConnect(Native Method)
即使加上
s.setReuseAddress(true);
也无用
手动关闭server程序,server发出fin,client处于time_wait
JoycedeMacBook:netty-test joyce$ netstat -an|grep 8888 大约1分钟
tcp4 0 0 127.0.0.1.8888 127.0.0.1.12123 TIME_WAIT
此时尽管2边进程已经完全退出,但连接和端口仍然在
重新启动server,和client,报
重启client,报:
Exception in thread "main" java.net.SocketException: Address already in use (connect failed)
at java.net.PlainSocketImpl.socketConnect(Native Method)
即使加上
s.setReuseAddress(true);
我们调整一下程序,让服务端能够响应fin,并将程序分别部署到mac和linux
public class Client { public static final int PORT = 12123; public static final int BUFFER_SIZE = 1024; public static void main(String []f) throws Exception { new Client().client(); } //客户端代码 public void client() throws Exception{ final String s2 = "localhost"; byte[] buffer; Socket s = new Socket(); /** * mac 对于Fin_wait2无用,对time_wait同样无用 mac * linux 只测试了time_wait,有用 */ s.setReuseAddress(true); s.bind(new InetSocketAddress(8888)); s.connect(new InetSocketAddress(s2,PORT)); int i = s.getInputStream().read(buffer = new byte[BUFFER_SIZE]); System.out.println(new String(buffer,0,i)); /** * 程序自然结束,close */ } }
public class Server { public static final int PORT = 12123; public static final int BUFFER_SIZE = 1024; public static void main(String [] f) throws IOException, InterruptedException { new Server().server(); } //服务端代码 public void server() throws IOException, InterruptedException{ ServerSocket ss = new ServerSocket(PORT); while(true) { Socket s = ss.accept(); s.getOutputStream().write("hello ".getBytes()); s.getOutputStream().flush(); /** * 以上代码不会发送fin包回应客户端的fin包 */ while (s.getInputStream().read(new byte[BUFFER_SIZE]) != -1) { ; }; /** * 显示close,发出fin包 */ s.close(); } } }
reuse这个参数测试结果如下
true false
mac fin_wait2 & time_wait 冲突 冲突
linux fin_wait2 & time_wait 有用 冲突
结论:
1 程序常规关闭下,进程即使没了,连接和端口占用仍然在,同样的情况还有redis:[专项]tcp状态机,为什么3次握手(很好)(done)
2 setReuseAddress对于Fin_wait2 和time_wait均没用(mac)都有用(linux)
3 服务端不显式close socket或关闭进程,不会发出fin包响应客户端的fin包,与https://www.cnblogs.com/dimmacro/p/4460848.html表述一致
4 程序正常结束会隐式close socket
5 客户端也可以绑定端口
*closewait对端fin wait2