logo头像

云影sky

TCP三次握手和四次挥手

TCP 三次握手(建立连接)

三次握手

三次握手

三次握手(Three-way Handshake),是指建立一个 TCP 连接时,需要客户端和服务器总共发送3个包。

三次握手的目的是连接服务器指定端口,建立 TCP 连接,并同步连接双方的序列号和确认号并交换 TCP 窗口大小信息。

第1次握手

客户端:

  • 发送 SYN(SYN=1),seq=x 包
  • 进入 SYN_SENT 状态

第2次握手

服务器:

  • 发送 ack=x + 1, seq=y, SYN=1, ACK=1
  • 进入 SYN_RECV 状态

第3次握手

客户端:

  • SYN=0, ACK=1, seq=x+1, ack=y+1

客户端此包发送完毕之后,客户端和服务器进入 ESTABLISHED(TCP 连接成功) 状态,完成三次握手。

完成三次握手,客户端与服务器开始传送数据。

TCP 四次挥手(断开连接)

四次挥手

四次挥手

第1次

客户端:

  • 发送报文 FIN=1, seq=u(等于前面已经发送过来的数据的最后一个字节的序号加1)
  • 进入 FIN-WAIT-1(终止等待1) (TCP 规定,FIN 报文段即使不携带数据,也要消耗一个序号)

第2次

服务器:

  • 收到连接释放报文
  • 发出确认报文,ACK=1, ack=u+1,并且带上自己的序列号 seq=v
  • 进入 CLOSE-WAIT(关闭等待) 状态

TCP 通知应用进程,客户端向服务器的方向就释放了,这时候处于半关闭状态,即客户端已经没有数据要发送了,但是服务器若发送数据,客户端依然要接收。这个状态还要持续一段时间,也就是整个 CLOSE-WAIT 状态持续的时间。

客户端:

  • 接收到服务器的确认请求
  • 进入 FIN-WAIT-2(终止等待2) 状态,等待服务器发送连接释放报文(这之前还需要接受服务器发送的最后的数据)。

第3次

服务器将最后的数据发送完毕后,就向客户端发送连接释放报文,由于在半关闭状态,服务器很可能又发送了一些数据。假定此时的序列号为 seq=w

服务器:

  • FIN=1, ACK=1, ack=u+1, seq=w(新的seq)
  • 进入 LAST-ACK 状态

第4次

客户端:

客户端接收到服务器的连接释放报文之后,必须发出确认。

  • ACK=1, ack=w+1, seq=u + 1
  • 客户端进入 TIME-WAIT(时间等待) 状态
  • 经过 2 MSL(Maximum Segment Lifetime)(报文最大生成时间),进入 CLOSED 状态

服务器:

  • 只要接收到了客户端发出的确认,立即进入 CLOSED 状态

服务器比客户端先进入 CLOSED 状态。

为什么三次握手

当 Server 收到 Client 端的 SYN 连接请求报文后,可以直接发送 SYN、ACK 报文。ACK 报文做应答, SYN 报文用来同步。

三次握手做好发送数据的准备工作,同时也要允许双方就初始序列号进行协商,这个序列号在握手过程中被发送和确认。

前两次握手进行同步和准备,第三次握手进入状态。

为什么不是两次握手呢?

如果一个连接请求在网络中跑得慢,超时了,这时客户端会重发请求,但这个跑的慢的客户端最后还是跑到了,然后服务端就接收了两个请求,然后全部回应就会创建两个连接,浪费资源。如果加入第三次验证,则客户端接收到一个服务端确认连接请求之后,后面再接收到确认连接请求就可以抛弃不管了。

为什么4次挥手

TCP 是双向的,所以需要在两个方向分别关闭,每个方向的关闭又需要请求和确认,所以一共就4次。

  • 客户端:服务器大哥我要停止传输了(FIN-WAIT-1)
  • 服务器:好的,我知道了(但可能我还有剩余的数据要据需传给你)(CLOSE-WAIT)
  • 客户端:服务器大哥已经知道我要断开连接了(FIN-WAIT-2)
  • 服务器:客户端小伙,我的数据传完了,我也不再给你发数据了(LAST-ACK)
  • 客户端:好的,大哥不传数据了,那我进入 TIME-WAIT 了,再进行 2 MSL 我就进入 CLOSED 啦
  • 服务器:那我断开连接了,bye bye(CLOSED)

如果已经建立了连接,但是客户端出现故障该怎么办

TCP 有一个保活计时器,客户端出现故障了,服务器是不能一直等下去浪费资源的的。

服务器每收到一次客户端请求后都会重新复位这个计时器,时间通常为 2h ,如果 2h 还没有收到客户端的任何数据,服务器就会发送一个探测报文段,以后每隔 75s 就会发送一次。若一连发送10个探测报文仍然没有反应,服务器就认为客户端出现故障,接着就关闭连接。

SYN 洪水攻击

SYN 洪水攻击发送在三次握手的前2次,攻击者发送 TCP SYN,SYN 是 TCP 三次握手中的第一个数据包,当服务器返回 ACK 后,攻击者不对其进行再确认,那这个连接就处于挂起状态,也就是所谓的半连接状态,服务器收不到再确认的话,还会重复发送 ACK 给攻击者,这会导致服务器资源被浪费。攻击者对服务器发送非常大量的这种 TCP 连接,由于大量半连接状态在服务器产生,服务器的资源消耗会不断增加,最后导致服务器故障。

服务器发送 ACK 之后处于 SYN_RECV 状态,服务器只有收到客户端的 ACK 之后,才会转入 ESTABLISHED 状态。

缩略词相关

  • 序列号 seq:占4个字节,用来标记数据段的顺序,TCP 把连接中发送的所有数据字段都编上一个序号,第一个字节的编号由本地随机产生;给字节编上序号之后,就给每一个报文段指派一个序号,序列号 seq 就是这个报文段中的第一个字节的数据编号。
    当发送一个数据时,数据时被拆成多个数据包来发送,序列号就是对每个数据包进行编号,这样接收方才能对数据包进行再次拼接。
    初始序列号是随机生成的,这样不一样的数据拆解保证就不会连接错误。
  • 确认号 ack:占4个字节,期待收到对方下一个报文段的第一个数据字节的序号。序列号表示报文段携带数据的第一个字节的编号;而确认号指的是期待接收到下一个字节的编号;即当前报文段最后一个字节的编号 +1 就是确认号。
  • 确认 ACK:占1位, 代表确认接收,不管是三次握手还是四次分手,在回应的时候会加上 ACK = 1,表示消息接收到了,并且在建立连接之后发送数据时,都需加上 ACK = 1,来表示数据接收成功了。
    仅当 ACK=1 时,确认号字段才有效。ACK=0 时,确认号无效。
  • 同步 SYN (Synchronize Sequence Numbers) 同步序列编号,代表请求创建连接,在三次握手中前两次要请求创建,表示这两次用于创建连接。
    建立连接时用于同步序号。当 SYN=1,ACK=0 时表示,这是一个连接请求报文段。若同意连接,则在响应报文段中让 SYN=1,ACK=1。 SYN=1 表示这是一个连接请求,或连接接受报文。
    SYN 这个标志位只有在 TCP 建立连接时才会被置1,握手完成后 SYN 标志位被置 0.
  • 终止 FIN: 表示请求关闭连接,在四次分手时,FIN 发了两遍。这是因为 TCP 的连接是双向的,所以一次 FIN 只能关闭一个方向。
    FIN=1 表示,此报文的发送方的数据已经发送完毕,并要求释放连接。

SYN、ACK、FIN 这些大写的单词表示标志位,其值只有0、1;小写的 seq、ack 表示序号。

SYN、ACK、FIN 存放在 TCP 的标志位。

字段 含义
ACK 确认号是否有效,一般置为1
SYN 请求建立连接,并在其序列号的字段进行序列号的初始值设定。建立连接,设为1
FIN 希望断开连接
URG 紧急指针是否有效。为1表示某一位需要被优先处理
PSH 提示接收端应用程序立即从 TCP 缓冲区把数据读走
RST 对方要求重新建立连接,复位

TCP 可靠传输的精髓

摘自 @车小胖谈网络

TCP可靠传输的精髓:TCP连接的一方A,由操作系统动态随机选取一个32位长的序列号(Initial Sequence Number),假设A的初始序列号为1000,以该序列号为原点,对自己将要发送的每个字节的数据进行编号,1001,1002,1003…,并把自己的初始序列号ISN告诉B,让B有一个思想准备,什么样编号的数据是合法的,什么编号是非法的,比如编号900就是非法的,同时B还可以对A每一个编号的字节数据进行确认。如果A收到B确认编号为2001,则意味着字节编号为1001-2000,共1000个字节已经安全到达。
同理B也是类似的操作,假设B的初始序列号ISN为2000,以该序列号为原点,对自己将要发送的每个字节的数据进行编号,2001,2002,2003…,并把自己的初始序列号ISN告诉A,以便A可以确认B发送的每一个字节。如果B收到A确认编号为4001,则意味着字节编号为2001-4000,共2000个字节已经安全到达。

依据对方传输回来的 ack 就可以判断自己本次传送的 seq 范围

相关参考