TCP是什么?

438 阅读6分钟

1. 什么是TCP?

TCP(传输控制协议,Transmission Control Protocol)是一种面向连接的、可靠的、基于字节流的传输层通信协议。"TCP连接"是指通过TCP协议建立的通信链接。

当两台计算机通过TCP协议进行通信时,它们首先需要建立一个连接。这个过程被称为"三次握手"(three-way handshake)。在通信完成后,它们会通过"四次挥手"(four-way handshake)的方式关闭这个连接。在连接存在的整个期间,TCP会确保数据的有序性和可靠性,即所有发送的数据都会按照发送的顺序被接收,且不会丢失或被重复接收。

每个TCP连接都有两个关键的组成部分:端口和IP地址。每台计算机都有一个唯一的IP地址,而端口则用于区分同一台计算机上的不同应用程序。因此,一个完整的TCP连接需要四个元素:源IP地址、源端口、目标IP地址和目标端口。

在TCP连接建立后,应用程序就可以开始在连接的两个端点之间发送和接收数据。TCP协议会确保这些数据按顺序且不丢失地到达另一端,即使网络环境有些不稳定也能保证这一点。这就是为什么说TCP提供的是一种可靠的服务。

2. 为什么TCP需要三次握手来建立连接和为什么TCP需要四次挥手来断开连接?

  1. 为什么TCP需要三次握手来建立连接

TCP(Transmission Control Protocol)是一种面向连接的、可靠的、基于字节流的传输层通信协议。在TCP/IP协议中,TCP协议提供可靠的、面向连接的服务,数据传输前需要进行三次握手建立连接。

三次握手过程如下:

  • 第一次握手:客户端发送连接请求报文段,将SYN位设置为1,Seq值为x;然后,进入SYN_SEND状态,等待服务器的确认;
  • 第二次握手:服务器收到SYN报文段,需要对这个SYN报文段进行确认,设置Ack值为x+1(表示确认客户端的Seq值);同时,自己也发送SYN请求信息,SYN值设为1,Seq值为y;服务器将上述所有信息放到一个报文(SYN+ACK报文)中,一并发送给客户端,此时服务器进入SYN_RCVD状态;
  • 第三次握手:客户端收到服务器的SYN+ACK报文,向服务器发送确认报文ACK(ACK报文段Ack值=y+1),此包发送完毕,客户端和服务器进入ESTABLISHED(已建立连接)状态,完成三次握手。

TCP三次握手的主要目的是同步双方的初始序列号以形成一条连接。同时也为了防止已失效的连接请求报文突然又传到了服务端,因而产生错误。

2. 为什么TCP需要四次挥手来断开连接

数据传输完毕后,TCP连接的拆除需要双方各发送一次FIN和确认。由于TCP连接是全双工的,所以每个方向都必须单独进行关闭。这就是为什么需要四次挥手而不是两次或三次的原因。

四次挥手过程如下:

  • 第一次挥手:客户端发送一个FIN,用来关闭客户端到服务器的数据传送,客户端进入FIN_WAIT_1状态。
  • 第二次挥手:服务器收到FIN后,发送一个ACK给客户端,确认序号为收到的序号加1,此时,服务器进入CLOSE_WAIT状态。
  • 第三次挥手:服务器关闭客户端的连接,发送一个FIN给客户端,服务器进入LAST_ACK状态。
  • 第四次挥手:客户端收到FIN后,客户端进入TIME_WAIT状态,发送一个ACK给服务器,确认序号为收到序号加1,然后等待2MSL后转到CLOSED状态。

这个过程是必要的,因为在网络通信中,报文可能会出现延迟、丢失等问题,四次挥手的过程确保了双方都能正确关闭连接。

以下是用Java伪代码解释TCP的三次握手和四次挥手的过程。这是简化的逻辑,并不是真实的Java代码,因为在实际开发中,这些底层的握手和挥手操作都是由操作系统的网络协议栈自动完成的。

假设我们有两个对象:客户端Client和服务器Server,它们都有一些方法来发送和接收消息。

  1. 三次握手
class Client {
    void sendSYN(Server server) {
        System.out.println("发送SYN");
        server.receiveSYN(this);
    }

    void receiveSYNACK(Server server) {
        System.out.println("收到SYN+ACK");
        sendACK(server);
    }

    void sendACK(Server server) {
        System.out.println("发送ACK");
        server.receiveACK(this);
    }
}

class Server {
    void receiveSYN(Client client) {
        System.out.println("收到SYN");
        sendSYNACK(client);
    }

    void sendSYNACK(Client client) {
        System.out.println("发送SYN+ACK");
        client.receiveSYNACK(this);
    }

    void receiveACK(Client client) {
        System.out.println("收到ACK");
        System.out.println("连接建立");
    }
}

public class Main {
    public static void main(String[] args) {
        Client client = new Client();
        Server server = new Server();
        client.sendSYN(server);
    }
}
  1. 四次挥手
class Client {
    void sendFIN(Server server) {
        System.out.println("发送FIN");
        server.receiveFIN(this);
    }

    void receiveACK(Server server) {
        System.out.println("收到ACK");
    }

    void receiveFIN(Server server) {
        System.out.println("收到FIN");
        sendACK(server);
    }

    void sendACK(Server server) {
        System.out.println("发送ACK");
        server.receiveACK(this);
        System.out.println("连接关闭");
    }
}

class Server {
    void receiveFIN(Client client) {
        System.out.println("收到FIN");
        sendACK(client);
    }

    void sendACK(Client client) {
        System.out.println("发送ACK");
        client.receiveACK(this);
    }

    void sendFIN(Client client) {
        System.out.println("发送FIN");
        client.receiveFIN(this);
    }

    void receiveACK(Client client) {
        System.out.println("收到ACK");
    }
}

public class Main {
    public static void main(String[] args) {
        Client client = new Client();
        Server server = new Server();
        client.sendFIN(server);
        server.sendFIN(client);
    }
}

以上代码模拟了三次握手和四次挥手的过程,以及在每个步骤中各方的行为。实际上,在真实的TCP协议中,这些操作会更复杂,包括序列号和确认号的处理、错误检查、流量控制等,都是由网络协议栈自动处理的。

3. 如果是二次握手或者三次挥手不行吗?

对于这个问题,我们需要理解TCP协议的设计理念和目标,那就是可靠性和双向性。TCP为了保证这两点,采取了三次握手和四次挥手的方式。

  1. 为什么不能二次握手?

二次握手不能满足TCP通信的可靠性需求。假设我们使用两次握手建立连接,那么就可能出现以下情况:一方(比如说客户端)发送了一个连接请求,但是这个请求在网络中延迟了一段时间后才到达服务器,而客户端可能已经认为请求丢失,于是重新开始新的连接请求。这时,服务器收到了延迟的请求,并建立了连接,然而这个连接已经没有对应的客户端了,会造成资源的浪费。而三次握手可以很好地解决这个问题。

  1. 为什么不能三次挥手?

这是因为TCP连接是全双工的(双向独立),每个方向都必须单独进行关闭。这就是为什么需要四次挥手而不是三次的原因。

客户端发送FIN报文段给服务器,表示其数据已经发送完毕,不再发送数据。但是,这并不代表客户端不接收数据,所以服务器可以继续发送数据。如果此时服务器也没有数据要发送,就会立即发送FIN报文段。如果只有三次挥手,就无法满足这种情况,可能会造成数据的丢失。

总的来说,三次握手和四次挥手是TCP为了实现其可靠性、双向性的设计,不足或者超出都可能导致TCP协议的核心价值——可靠性被破坏。