TCP is the abbreviation for Transmission Control Protocol. It is one of the core protocols in the internet protocol suite and provides a reliable protocol to communicate in computer networks. Nearly every Internet-connected device “talks” TCP and the whole Internet relies on it.
Display connections on Linux
To display listeners and connections on Linux you’ve to use the netstat or ss command. While older Linux boxes only support netstat, newer Linux distributions use netstat and ss in parallel. However, with the introduction of ss, netstat is marked as deprecated.
# Display only TCP sockets / connections. netstat -t ss -t # Display only UDP sockets / connections. netstat -u ss -u
There are a lot of options and here are some common ones:
- -l displays only listeners
- -p shows the PID and program name for each socket
- -e shows an extended output (e.g. user and inodes)
- -n show numeric output instead of hostnames, port or usernames
Combing all these options will result in an mnemonic for german speaking people:
netstat -tulpen ss -tulpen
The word Tulpen means tulips in german 😉
TCP connection states
The most common states are relatively simple: LISTEN, CLOSED and ESTABLISHED. Most of the developers don’t need to know more about TCP states, because this is what an application really cares about. However, there’s more going on under the hood of the TCP stack.
How a connection is initiated
Let’s have a look into how a TCP connection will be initiated.
Before a TCP connection can be opened, you need to have a server with a listener. The listener will listen on incoming connections on a specific port. This state is represented as LISTEN.
- When a new TCP connection is opened, the client (initiator) sends a SYN packet to the server (receiver) and updates its state to SYN-SENT.
- The server will then send a SYN-ACK in reply to the client which changes its connection state to SYN-RECEIVED.
- If everything worked properly, the client will reply with an ACK and the connection is marked as ESTABLISHED on both end-point.
Now the client and the server are ready to transfer data.
How a connection is terminated
Whenever a client or server is finished with the data exchange, they can terminate their connection. However, they can’t just drop it because their other end-point needs to know about the termination. So instead of just dropping the connection immediately and therefor no longer sending packets, one end-point can initiate the termination. Closing a connection is a four-way handshake, because each end-point is terminating the connection independently. It doesn’t matter which end-point starts the termination, because both of them can start it at any point. But let’s say the client starts the termination:
- The client sends a FIN packet to the server and updates its state to FIN-WAIT-1.
- The server receives the termination request from the client and responds with an ACK. After the reply the server will be in a CLOSE-WAIT state.
- As soon as the client receives the reply from the server, it will go to the FIN-WAIT-2 state.
We already wrote that the termination of a connection is a four-way handshake. So while the connection is terminated from a client point of view, the server has to terminate its connection as well. This happens directly after the server sent its last ACK.
- The server is still in the CLOSE-WAIT state and it will independently follow up with a FIN, which updates the state to LAST-ACK.
- Now the client receives the termination request and replies with an ACK, which results in a TIME-WAIT state.
- The server is now finished and sets the connection to CLOSED immediately.
- The client stays in the TIME-WAIT state for a maximum of four minutes (defined by RFC793 and the maximum segment lifetime) before setting the connection to CLOSED as well.
There’s also a three-way handshake alternative available, which occurs when the server sends its ACK+FIN in one single packet (server goes from ESTABLISHED to LAST-ACK in a single step).