实操
public static void main(String[] args) throws IOException, InterruptedException {
ServerSocket serverSocket = new ServerSocket(8899);
// 故意的睡眠
Thread.sleep(Long.MAX_VALUE);
for (; ; ) {
Socket acceptSocket = serverSocket.accept();
System.out.println(acceptSocket);
for (; ; ) {
int read = acceptSocket.getInputStream().read();
if (read == -1) {
acceptSocket.close();
System.out.println(acceptSocket + " 下线");
break;
}
}
}
}
- 如上代码,我起了一个
java-server,在执行new ServerSocket(8899)之后,就开始睡眠了。不过在这时操作系统已经产生了一个listen socket
➜ ~ netstat -an | grep 8899
tcp46 0 0 *.8899 *.* LISTEN
- 此时我用
nc去连这个Java server通过tcpdump发现三次握手了
12:35:54.574451 IP node01.53646 > 192.168.31.120.ospf-lite: Flags [S], seq 1325316749, win 14600, options [mss 1460,sackOK,TS val 65057503 ecr 0,nop,wscale 6], length 0
12:35:54.574964 IP 192.168.31.120.ospf-lite > node01.53646: Flags [S.], seq 3221323920, ack 1325316750, win 64240, options [mss 1460], length 0
12:35:54.574977 IP node01.53646 > 192.168.31.120.ospf-lite: Flags [.], ack 3221323921, win 14600, length 0
- 这时候再去看下端口状态,已经建立好连接了。不过此时
Java server在睡觉
➜ ~ netstat -an | grep 8899
tcp4 0 0 192.168.31.120.8899 192.168.31.120.63723 ESTABLISHED
tcp4 0 0 192.168.31.120.63723 192.168.31.120.8899 ESTABLISHED
tcp46 0 0 *.8899
- 这时候把
nc Ctrl-C,关掉客户端进程,通过抓包可以发现四次挥手中的前两次。
12:36:13.475971 IP node01.53646 > 192.168.31.120.ospf-lite: Flags [F.], seq 1325316750, ack 3221323921, win 14600, length 0
12:36:13.476368 IP 192.168.31.120.ospf-lite > node01.53646: Flags [.], ack 1325316751, win 64239, length 0
- 在看下端口状态
➜ ~ netstat -an | grep 8899
tcp4 0 0 192.168.31.120.8899 192.168.31.120.63723 CLOSE_WAIT
tcp4 0 0 192.168.31.120.63723 192.168.31.120.8899 FIN_WAIT_2
tcp46 0 0 *.8899 *.* LISTEN
分析
FIN_WAIT_2&CLOSE_WAIT
结合这两个状态以及抓包结果进行分析。
- 客户端发出了
FIN,服务端收到并且回复了ACK, 由于服务器阻塞,导致服务端没能发出 第三次挥手的FIN并且状态就是CLOSED-WAIT了。客户端也在等服务端发FIN,没等来 所以状态是FIN-WAIT2。
java-server如何知道client下线呢
服务器端拿到输入流进行read。当客户端断开连接后,服务器端read一直有返回值是-1。
ServerSocket serverSocket = new ServerSocket(8899);
for (; ; ) {
Socket acceptSocket = serverSocket.accept();
System.out.println(acceptSocket);
for (; ; ) {
int read = acceptSocket.getInputStream().read();
System.out.println(read);
}
}
此时这个-1是否恰恰就是客户端断开连接的标志呢?server得知这个消息后需要进行
int read = acceptSocket.getInputStream().read();
if (read == -1) {
acceptSocket.close();
}
执行close,服务端发送了FIN,客户端也随之回复了ACK。四次挥手完毕。