Understanding and mastering the three handshakes and four breakups of TCP is a basic skill for every programmer, so let’s start with the TCP header.

The TCP header

TCP works at the transport layer, providing reliable transport from application to application. To learn TCP, start with the TCP header:

  • Source Port and Destination Port: 16-bit Source Port and Destination Port respectively. The IP address is used to distinguish different hosts. The source port number and destination port number combined with the source IP address and destination IP address in the IP header can uniquely determine a TCP connection.
  • Sequence Number: identifies the byte stream sent from the TCP source to the TCP receiver. It indicates the Sequence Number of the first byte in the packet segment in the data stream.It is mainly used to solve the problem of out-of-order network reports;
  • This Acknowledgment Number contains the next Acknowledgment that the end sending the Acknowledgment is expected to receive, so the Acknowledgment Number should be the last successfully received data byte Acknowledgment Number plus 1. However, this confirmation sequence number field is valid only if the ACK flag in the flag bit (described below) is 1.Mainly used to solve the problem of packet loss;
  • Offset: Gives the number of 32-bit words in the header. This value is needed because the length of the optional fields is variable. This field takes up to 4 bits, so TCP has a maximum of 60 bytes of header. However, there are no optional fields and the normal length is 20 bytes;
  • TCP Flags: There are 6 flag bits in the TCP header, many of which can be set to 1 at the same time. They are used to control the TCP state machine: URG, ACK, PSH, RST, SYN, and FIN. Each flag bit has the following meaning:
    • URG: This flag indicates that the TCP packet’s emergency pointer field (more on that later) is valid, and is used to ensure that the TCP connection is not interrupted and to urge the mid-tier device to process the data as quickly as possible.
    • ACK: This flag indicates that the reply field is valid, that is, the TCP reply number mentioned above will be included in the TCP packet. There are two values: 0 and 1. If the value is 1, the response field is valid; otherwise, it is 0.
    • PSH: This flag bit represents the Push operation. The Push operation means that the data packet is sent to the application immediately after it arrives at the receiving end, instead of queuing in the buffer.
    • RST: This flag indicates a connection reset request. It is used to reset faulty connections and reject faulty and invalid packets.
    • SYN: indicates the synchronization sequence number, which is used to establish a connection. The SYN flag bit and ACK flag bit are used together. When a connection request is made, SYN=1 and ACK=0. When the connection is answered, SYN=1, ACK=1; Packets with this flag are often used for port scanning. The scanner sends a packet containing only SYN. If the host responds with a packet, it indicates that the host has this port. However, this scanning method is only the first handshake of the TCP three-way handshake. Therefore, the success of this scanning method indicates that the scanned machine is not very secure. A secure host will force a connection to perform the TCP three-way handshake strictly.
    • FIN: Indicates that the sender reaches the end of data transmission, that is, data transmission is complete and no data can be transmitted. After the TCP packet with the FIN flag bit is sent, the connection is disconnected. Packets with this flag are also often used for port scanning.
  • Window: the size of a Window, known as a sliding Window, used for flow control

Okay, let’s get down to business.

TCP three handshakes and four breakups

In order to analyze the details of TCP handshake and break up, we wrote the server code and client code, run the following program, and capture the packet, through the capture of the above handshake and break up process. The following is the program code used to analyze the TCP three-way handshake and four-way breakup process:

Server code:

Server-side code written by Rust:

use std::net::{TcpListener, TcpStream};
use std::io::prelude::*;
use std::thread;

fn main() {{let listener = TcpListener::bind("127.0.0.1:33333").unwrap();
	    let (mut stream, addr) = listener.accept().unwrap();
	    println!("tcp accept from {:? }", addr);
	    let mut buf = [0; 1024];
	    let size = stream.read(&mut buf).unwrap();
	    println!("receive from remote {} bytes data.", size);
	    thread::sleep_ms(1000);
	}
	thread::sleep_ms(6*1000);
}
Copy the code

Or server (Windows) program code written in c++ :

// tcpServerSimple. CPP: defines the entry point for the console application.
#include "stdafx.h"
#include<WinSock2.h>
#include<stdlib.h>
#include<WS2tcpip.h>
#include<string>
#include<iostream>
using namespace std;

#pragma comment(lib, "ws2_32.lib")
#define _WINSOCK_DEPRECATED_NO_WARNINGS

int main(a)
{
	WSADATA wsaData;
	if (WSAStartup(MAKEWORD(2.2), &wsaData) ! =0) {
		cout << "Failed to load Winsock" << endl;
		return - 1;
	}

	SOCKET sockServer = socket(AF_INET, SOCK_STREAM, 0);

	SOCKADDR_IN addrServer;
	addrServer.sin_family = AF_INET;
	addrServer.sin_port = htons(33333);
	addrServer.sin_addr.S_un.S_addr = htonl(INADDR_ANY);

	if (SOCKET_ERROR == bind(sockServer, (LPSOCKADDR)&addrServer, sizeof(SOCKADDR_IN))) {
		cout << "Failed bind:" << WSAGetLastError() << endl;
		return - 1;
	}

	if (SOCKET_ERROR == listen(sockServer, 10)) {
		cout<<"Listen failed:"<< WSAGetLastError() << endl;
		return - 1;
	}

	SOCKADDR_IN addrClient;
	int len = sizeof(SOCKADDR);

	SOCKET sockConn = accept(sockServer, (SOCKADDR*)&addrClient, &len);
	if (SOCKET_ERROR == sockConn) {
		cout << "Accept failed:" << WSAGetLastError() << endl;
		return - 1;
	}
	char addrBuf[20] = { '\ 0' };
	inet_ntop(AF_INET, (void*)&addrClient.sin_addr, addrBuf, 16);
	cout << "Accept from " << addrBuf << endl;

	char recvBuf[1024];
	memset(recvBuf, 0.sizeof(recvBuf));
	int size = recv(sockConn, recvBuf, sizeof(recvBuf), 0);
	cout << "received " << size << " from remote" << endl;

	Sleep(1000);
	closesocket(sockConn);
	closesocket(sockServer);

	WSACleanup();
	system("pause");

    return 0;
}
Copy the code

Client code:

Rust implements the following:

use std::io::prelude::*;
use std::net::TcpStream;
use std::thread;

fn main() {{let mut stream = TcpStream::connect("192.168.2.210:33333").unwrap();
        let n = stream.write(&[1.2.3.4.5.6.7.8.9.10]).unwrap();
        println!("send {} bytes to remote node, waiting for end.", n);
        thread::sleep_ms(1000);
    }
    thread::sleep_ms(10*60*1000);
}
Copy the code

TCP establishes a connection – the three-way handshake

TCP is connection-oriented. Before either party sends data to the other, a connection must be established between the two parties. In TCP/IP, TCP provides a reliable connection service that is initialized with a three-way handshake. The purpose of the three-way handshake is to synchronize the serial numbers and confirmation numbers of the two connected parties and exchange TCP window size information. The following uses the preceding programs and the Wireshark to analyze the TCP connection process.

After running the server program, the server program enters the listening state LISTEN.

First: The client sends a SYN to the server, and the client enters the SYN_SENT state (the intermediate states in the handshake are very short and hard to see, most see LISTEN and ESTABLISH). As shown below:

The server receives a SYN and responds with a SYN+ACK to enter the SYN+RCVD state. This state is too short to see.

Third: After receiving a SYN+ACK, the client replies that it enters the ESTABLISH state. Packet capture (ACK)

After receiving the ACK, the server enters the ESTABLISH state, completes the handshake, and establishes the connection.

TCP disconnection process – four breakups

When a client establishes a TCP connection with a server through a three-way handshake, it is necessary to disconnect the TCP connection when the data transfer is complete. For TCP disconnections, there is the mysterious “four breakups”.

First time: Host A (either the client or the server) sends the connection release FIN packet. At this time, host A enters the FIN_WAIT_1 state, indicating that host A has no data to send to host B. Caught FIN:

Second: Host B receives A FIN packet from host A and sends an ACK packet to host A. After receiving the ACK, host A enters the FIN_WAIT_2 state, and host B enters the CLOSE_WAIT state. Caught an ACK:

The third time:
FIN
LAST_ACK
FIN

Fourth time: Host A receives the FIN packet from host B and sends an ACK packet to host B. Then host A enters the TIME_WAIT state. Host B closes the connection after receiving an ACK packet from host A. If host A does not receive A response after waiting for 2MSL (maximum packet lifetime), the Server is shut down. In this case, host A shuts down the connection. Caught an ACK:

At this point, the four TCP breakups are complete and the connection is disconnected.

Finally, see the result of packet capture from the code:

Three handshakes. Why?

Why three handshakes when TCP establishes a connection? Not twice? It is said in Xie Xiren’s Computer Network:

An error occurs in case an invalid connection request segment is suddenly sent to the server.

In the book also gives an example, as follows:

Invalid connection request segment Is generated in this case: The first connection request segment sent by the client is not lost, but is detained on a network node for a long time. As a result, it is delayed until a certain time after the connection is released. Originally, this is an invalid packet segment. However, after the server receives the invalid connection request packet segment, it mistakenly thinks it is a new connection request sent by the client. Then the client sends a confirmation message to agree to establish a connection. Assuming that the “three-way handshake” is not used, a new connection is established as soon as the server sends an acknowledgement. Since the client does not send a connection request, it ignores the server’s confirmation and does not send data to the server. However, the server assumes that a new connection has been established and waits for data from the client. As a result, many of the server’s resources are wasted. The three-way handshake prevents this from happening. For example, the client does not issue an acknowledgement to the server’s acknowledgement. When the server receives no acknowledgement, it knows that the client has not requested a connection.”

This makes sense and prevents the server side from wasting resources by waiting.

However, the three-way handshake is not perfect, and there is also a problem, namely, SYN Flood attack. The main attack means is to send a large number of SYN requests to the server for connection, and the server responds with SYN+ACK to the client. However, the client does not send the final ACK to the server. A large number of resources are occupied on the server. I will not go into details here.

Four breakups? Why?

TCP is in full-duplex mode, which is the key to understanding the four breakups. When A sends A FIN packet, it only means that A has no data to send, but does not mean that B does not need to send data to A. In this case, A can still receive data from B. When B returns an ACK packet, it indicates that it knows that NO data is sent from A, but B can still send data to A. So breaking up twice is not an option. When B no longer needs to send data to A, it sends A FIN packet to A to tell A that IT has no data to send either. Then the TCP connection is interrupted.

Status during the four breakups:

state explain
FIN_WAIT_1 The FIN_WAIT_1 and FIN_WAIT_2 states are both waiting for FIN packets from the peer. The difference between the two states is as follows: When the SOCKET is in the ESTABLISHED state, it tries to close the connection and sends a FIN packet to the peer. Then the SOCKET enters the FIN_WAIT_1 state. After the peer party responds to an ACK message, it enters the FIN_WAIT_2 state. Of course, under normal circumstances, the peer party should respond to an ACK message immediately, so the FIN_WAIT_1 state is difficult to see, and the FIN_WAIT_2 state can be seen sometimes by using netstat. (Active Party)
FIN_WAIT_2 This state has been explained in detail above. In fact, a SOCKET in the FIN_WAIT_2 state is a semi-connection, which means that one party wants to close the connection but also tells the other party that I have some data to send to you (ACK message) and will close the connection later. (Active Party)
CLOSE_WAIT The meaning of this state is actually waiting for the shutdown. How do you understand that? When the peer sends a FIN packet to the peer after closing a SOCKET, the system responds with an ACK packet and enters CLOSE_WAIT state. The next thing you really need to consider is to see if you have any data to send to the peer. If not, you can close the SOCKET and send the FIN packet to the peer, closing the connection. So when you are in CLOSE_WAIT, all you need to do is wait for you to close the connection. (Passive)
LAST_ACK This status is relatively easy to understand. After a party sends a FIN packet, it waits for the ACK packet from the other party. After an ACK packet is received, the system enters the CLOSED state. (Passive)
TIME_WAIT It indicates that a FIN packet is received and an ACK packet is sent. The state can be CLOSED after 2MSL. If the FIN_WAIT_1 state receives a packet with both the FIN flag and ACK flag, you can enter the TIME_WAIT state without going through the FIN_WAIT_2 state. (Active Party)
CLOSED Indicates that the connection is down.

Why wait 2MSL in TIME_WAIT state? After receiving a FIN packet from the server, the client enters the CLOSED state and waits for the time set by a timer 2MSL. There are two reasons to do this:

  • First, ensure that the last acknowledgement message arrives. If USER B does not receive the confirmation packet sent by user A, user B resends the connection release request packet. User A waits for A period of time to process this situation.
  • Second, the purpose of waiting for a period of time is to make all the packets generated during the duration of the connection disappear from the network, so that the next new connection does not have the old connection request packets.

Welcome to pay attention to wechat public number, regularly push TCP/IP, back-end development, blockchain, distributed, Linux, Rust and other technical articles!