TCP协议入门

610 阅读4分钟

TCP(Transmission Control Protocol),又叫传输控制协议。 TCP协议是面向连接的,可靠的,基于字节流的传输协议。在基于 TCP 进行通信时,通信双方需要先建立一个 TCP 连接,建立连接需要经过三次握手,断开连接的时候需要经过四次挥手。

TCP头部

对于 TCP 头部来说,以下几个字段是很重要的:

  • 序列号 (Sequence number),这个序号保证了 TCP 传输的报文都是有序的,对端可以通过序号顺序的拼接报文
  • 确认号 (Acknowledgement Number),这个序号表示数据接收端期望接收的下一个字节的编号是多少,同时也表示上一个序号的数据已经收到
  • 窗口大小 (Window Size),表示还能接收多少字节的数据,用于流量控制
  • 标识符(TCP Flag) 六个标识符,紧急URG,确认ACK,推送PSH(PuSH),复位RST(ReSeT),同步SYN,终止FIN(FINis):
    • ACK=1 :该字段为一表示确认号字段有效。此外,TCP 还规定在连接建立后传送的所有报文段都必须把 ACK 置为一。
    • SYN=1:当SYN=1,ACK=0时,表示当前报文段是一个连接请求报文。当SYN=1,ACK=1时,表示当前报文段是一个同意建立连接的应答报文。
    • FIN=1:该字段为一表示此报文段是一个释放连接的请求报文。
    • URG=1 : 该字段为一表示本数据报的数据部分包含紧急信息,是一个高优先级数据报文,此时紧急指针有效。紧急数据一定位于当前数据包数据部分的最前面,紧急指针标明了紧急数据的尾部。
    • PSH=1 :该字段为一表示接收端应该立即将数据 push 给应用层,而不是等到缓冲区满后再提交。
    • RST=1:该字段为一表示当前 TCP 连接出现严重问题,可能需要重新建立 TCP 连接,也可以用于拒绝非法的报文段和拒绝连接请求。

TCP 三次握手

  • 第一次握手
    • SYN = 1, seq(client) = x
    • 客户端向服务端发送连接请求报文段。该报文段中包含自身的数据通讯初始序号。请求发送后,客户端便进入 SYN-SENT 状态。
  • 第二次握手
    • SYN = 1,ACK = 1,确认序号 = x+1, seq(server) = y
    • 服务端收到连接请求报文段后,如果同意连接,则会发送一个应答,该应答中也会包含自身的数据通讯初始序号,发送完成后便进入 SYN-RECEIVED 状态
  • 第三次握手
    • ACK = 1,确认序号 = y+1, seq(client) = x + 1
    • 客户端收到连接同意的应答后,还要向服务端发送一个确认报文。客户端发完这个报文段后便进入ESTABLISHED 状态,服务端收到这个应答后也进入 ESTABLISHED 状态,此时连接建立成功。

为什么不采用两次握手

主要是为了防止已经失效的连接请求报文突然又传送到了服务器,从而产生错误。

假设有这样一种场景, 客户端发送的第一个请求连接并且没有丢失,但是被滞留的时间太长。由于TCP的客户端迟迟没有收到确认报文,以为服务器没有收到,此时重新向服务器发送报文。 而现在第一个请求到达服务端,这个请求已经报废了,但是又会建立连接。

如果采用的是三次握手,就算是那一次失效的报文传送过来了,服务端接受到了那条失效报文并且回复了确认报文,但是客户端不会再次发出确认。由于服务器收不到确认,就知道客户端并没有请求连接。

TCP 四次握手

为什么建立连接只要三次,断开却要四次呢

这是因为服务端的LISTEN状态下的SOCKET当收到SYN报文的连接请求后,它可以把ACK和SYN(ACK起应答作用,而SYN起同步作用)放在一个报文里来发送。但关闭连接时,当收到对方的FIN报文通知时,它仅仅表示对方没有数据发送给你了;但未必你所有的数据都全部发送给对方了,所以你可能未必会马上会关闭SOCKET,也即你可能还需要发送一些数据给对方之后,再发送FIN报文给对方来表示你同意现在可以关闭连接了,所以它这里的ACK报文和FIN报文多数情况下都是分开发送的。