Search code examples
javasocketsnetwork-programmingtcp

ss command shows TIME_WAIT instead of CLOSE_WAIT which does not fit logically


I am trying to understand TIME_WAIT and CLOSE_WAIT.

I have opened a socket connection via Chrome console, which connects to my Java WebSocket server running locally - then closed it:

var webSocket = new WebSocket('ws://127.0.0.1:1234/support');
webSocket.close();

What I expect to see when I run sudo ss -apn|grep ':1234' is CLOSE_WAIT as it the client who has closed the connection, and should be first to send fin packet, which would have changed server socket status to CLOSE_WAIT, however, I see differently.

Tuesday 17 May 2022 06:23:21 PM IST

tcp6       0      0 :::1234                 :::*                    LISTEN      86008/java          
tcp6       0      0 127.0.0.1:1234          127.0.0.1:60672         TIME_WAIT   -   

Can somebody please explain what is happening?

Update 1:

masood@masood-ThinkPad-L450:~/Desktop/code/current/chat_server11$ sudo tcpdump -i lo port 1234
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on lo, link-type EN10MB (Ethernet), capture size 262144 bytes



21:39:58.376375 IP6 ip6-localhost.34196 > ip6-localhost.1234: Flags [S], seq 969747395, win 65476, options [mss 65476,sackOK,TS val 3310041060 ecr 0,nop,wscale 7], length 0
21:39:58.376393 IP6 ip6-localhost.1234 > ip6-localhost.34196: Flags [S.], seq 630365028, ack 969747396, win 65464, options [mss 65476,sackOK,TS val 3310041060 ecr 3310041060,nop,wscale 7], length 0
21:39:58.376410 IP6 ip6-localhost.34196 > ip6-localhost.1234: Flags [.], ack 1, win 512, options [nop,nop,TS val 3310041060 ecr 3310041060], length 0
21:39:58.378183 IP6 ip6-localhost.34196 > ip6-localhost.1234: Flags [P.], seq 1:535, ack 1, win 512, options [nop,nop,TS val 3310041062 ecr 3310041060], length 534
21:39:58.378197 IP6 ip6-localhost.1234 > ip6-localhost.34196: Flags [.], ack 535, win 508, options [nop,nop,TS val 3310041062 ecr 3310041062], length 0
21:39:58.398870 IP6 ip6-localhost.1234 > ip6-localhost.34196: Flags [P.], seq 1:130, ack 535, win 512, options [nop,nop,TS val 3310041083 ecr 3310041062], length 129
21:39:58.398890 IP6 ip6-localhost.34196 > ip6-localhost.1234: Flags [.], ack 130, win 511, options [nop,nop,TS val 3310041083 ecr 3310041083], length 0





21:40:08.678597 IP6 ip6-localhost.34196 > ip6-localhost.1234: Flags [P.], seq 535:541, ack 130, win 512, options [nop,nop,TS val 3310051362 ecr 3310041083], length 6
21:40:08.678618 IP6 ip6-localhost.1234 > ip6-localhost.34196: Flags [.], ack 541, win 512, options [nop,nop,TS val 3310051362 ecr 3310051362], length 0
21:40:08.679275 IP6 ip6-localhost.1234 > ip6-localhost.34196: Flags [P.], seq 130:132, ack 541, win 512, options [nop,nop,TS val 3310051363 ecr 3310051362], length 2
21:40:08.679293 IP6 ip6-localhost.34196 > ip6-localhost.1234: Flags [.], ack 132, win 512, options [nop,nop,TS val 3310051363 ecr 3310051363], length 0
21:40:08.679438 IP6 ip6-localhost.1234 > ip6-localhost.34196: Flags [F.], seq 132, ack 541, win 512, options [nop,nop,TS val 3310051363 ecr 3310051363], length 0
21:40:08.679487 IP6 ip6-localhost.34196 > ip6-localhost.1234: Flags [F.], seq 541, ack 133, win 512, options [nop,nop,TS val 3310051363 ecr 3310051363], length 0
21:40:08.679506 IP6 ip6-localhost.1234 > ip6-localhost.34196: Flags [.], ack 542, win 512, options [nop,nop,TS val 3310051363 ecr 3310051363], length 0

I tried to capture packets using tcpdump and I can clearly see that server is sending directly FIN+ACK without receiving FIN.I am totally confused.


Solution

  • as it the client who has closed the connection, and should be first to send fin packet

    That is an incorrect assumption. When the client calls close() on a fully established WebSocket connection, close() will perform a handshake that causes the server to close the TCP connection first (send the initial FIN), rather than the client.

    This is described in the WebSocket protocol spec, RFC 6455, Section 7, "Closing the Connection":

    7.1.1. Close the WebSocket Connection

    To Close the WebSocket Connection, an endpoint closes the underlying TCP connection. An endpoint SHOULD use a method that cleanly closes the TCP connection, as well as the TLS session, if applicable, discarding any trailing bytes that may have been received. An endpoint MAY close the connection via any means available when necessary, such as when under attack.

    The underlying TCP connection, in most normal cases, SHOULD be closed first by the server, so that it holds the TIME_WAIT state and not the client (as this would prevent it from re-opening the connection for 2 maximum segment lifetimes (2MSL), while there is no corresponding server impact as a TIME_WAIT connection is immediately reopened upon a new SYN with a higher seq number). In abnormal cases (such as not having received a TCP Close from the server after a reasonable amount of time) a client MAY initiate the TCP Close. As such, when a server is instructed to Close the WebSocket Connection it SHOULD initiate a TCP Close immediately, and when a client is instructed to do the same, it SHOULD wait for a TCP Close from the server.

    As an example of how to obtain a clean closure in C using Berkeley sockets, one would call shutdown() with SHUT_WR on the socket, call recv() until obtaining a return value of 0 indicating that the peer has also performed an orderly shutdown, and finally call close() on the socket.

    7.1.2. Start the WebSocket Closing Handshake

    To Start the WebSocket Closing Handshake with a status code (Section 7.4) /code/ and an optional close reason (Section 7.1.6) /reason/, an endpoint MUST send a Close control frame, as described in Section 5.5.1, whose status code is set to /code/ and whose close reason is set to /reason/. Once an endpoint has both sent and received a Close control frame, that endpoint SHOULD Close the WebSocket Connection as defined in Section 7.1.1.

    As well as the WebSockets standard, close() method:

    1. Run the first matching steps from the following list: