「研」 TCP 连接

本文最后更新于:10 个月前

本文总结 TCP 的三次握手和四次挥手,以及 TLS/SSL 的四次握手过程。

1 TCP 连接的建立(三次握手)

最开始的时候客户端和服务器都是处于 CLOSED 状态。主动打开连接的为客户端,被动打开连接的是服务器。

三次握手

  1. TCP 服务器进程先创建传输控制块 TCB,准备接受连接请求,此时服务器就进入了 LISTEN(监听)状态。

  2. TCP 客户进程也是先创建传输控制块 TCB,然后向服务器发出连接请求报文。报文首部中的同部位 SYN=1,同时选择一个初始序列号 seq=x。此时,TCP 客户端进程进入了 SYN-SENT(同步已发送状态)状态。TCP 规定,SYN 报文段(SYN=1 的报文段)不能携带数据,但需要消耗掉一个序号。

  3. TCP 服务器收到请求报文后,如果同意连接,则发出确认报文。确认报文中 ACK=1,SYN=1,确认号是 ack=x+1,同时也要为自己初始化一个序列号 seq=y,此时,TCP 服务器进程进入了 SYN-RCVD(同步收到)状态。这个报文也不能携带数据,但是同样要消耗一个序号。

  4. TCP 客户进程收到确认后,还要向服务器给出确认。确认报文的 ACK=1,ack=y+1,自己的序列号 seq=x+1,此时,TCP连接建立,客户端进入 ESTABLISHED(已建立连接)状态。TCP 规定,ACK 报文段可以携带数据,如果不携带数据则不消耗序号

  5. 当服务器收到客户端的确认后也进入 ESTABLISHED 状态,此后双方就可以开始通信了。

为什么 TCP 客户端最后还要发送一次确认呢?

主要防止已经失效的连接请求报文传送到服务器,重复历史连接的初始化。

如果使用的是两次握手建立连接,假设有这样一种场景:客户端发送了第一个请求连接并且没有丢失,只是因为在网络结点中滞留的时间太长了,由于 TCP 的客户端迟迟没有收到确认报文,重新向服务器发送这条报文,此后客户端和服务器经过两次握手完成连接,传输数据,然后关闭连接。此时滞留的请求连接,网络通畅到达了服务器,这个报文本该是失效的,但是两次握手的机制将会让客户端和服务器再次建立连接,这将导致不必要的错误和资源的浪费。

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

2 TCP 连接的释放(四次挥手)

数据传输完毕后,双方都可释放连接。最开始的时候,客户端和服务器都是处于 ESTABLISHED 状态,然后客户端主动关闭,服务器被动关闭。

四次挥手

  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 状态持续的时间。

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

  4. 服务器将最后的数据发送完毕后,就向客户端发送连接释放报文,FIN=1,ack=u+1,由于在半关闭状态,服务器很可能又发送了一些数据,假定此时的序列号为 seq=w,此时,服务器就进入了 LAST-ACK(最后确认)状态,等待客户端的确认。

  5. 客户端收到服务器的连接释放报文后,必须发出确认,ACK=1,ack=w+1,而自己的序列号是 seq=u+1,此时,客户端就进入了 TIME-WAIT(时间等待)状态。注意此时 TCP 连接还没有释放,必须经过 2MSL(最长报文段寿命)的时间后,当客户端撤销相应的 TCB 后,才进入 CLOSED 状态。

  6. 服务器只要收到了客户端发出的确认,立即进入 CLOSED 状态。同样,撤销 TCB 后,就结束了这次的 TCP 连接。可以看到,服务器结束 TCP 连接的时间要比客户端早一些。

为什么客户端最后还要等待 2 MSL?

MSL(Maximum Segment Lifetime),TCP 允许不同的实现可以设置不同的 MSL 值。

第一,保证客户端发送的最后一个 ACK 报文能够到达服务器,因为这个 ACK 报文可能丢失,站在服务器的角度看来,我已经发送了 FIN+ACK 报文请求断开了,客户端还没有给我回应,应该是我发送的请求断开报文它没有收到,于是服务器又会重新发送一次,而客户端就能在这个 2 MSL 时间段内收到这个重传的报文,接着给出回应报文,并且会重启 2 MSL 计时器。

第二,防止类似与“三次握手”中提到了的“已经失效的连接请求报文段”出现在本连接中。客户端发送完最后一个确认报文后,在这个 2 MSL 时间中,就可以使本连接持续的时间内所产生的所有报文段都从网络中消失。这样新的连接中不会出现旧连接的请求报文。

为什么建立连接是三次握手,关闭连接确是四次挥手呢?

建立连接的时候, 服务器在 LISTEN 状态下,收到建立连接请求的 SYN 报文后,把 ACK 和 SYN 放在一个报文里发送给客户端。

而关闭连接时,服务器收到对方的 FIN 报文时,仅仅表示对方不再发送数据了但是还能接收数据,而自己也未必全部数据都发送给对方了,所以己方可以立即关闭,也可以发送一些数据给对方后,再发送 FIN 报文给对方来表示同意现在关闭连接,因此,己方 ACK 和 FIN 一般都会分开发送,从而导致多了一次。

3 TLS/SSL 握手

SSL(Secure Socket Layer 安全套接层)是 TCP/IP 协议中位于 HTTP 之下,TCP 之上的一个可选协议层。起初 HTTP 在传输数据时使用的是明文,是不安全的。为了解决这个隐患,网景(Netscap)公司推出了 SSL,使得报⽂能够加密传输。

HTTPS(HTTP+SSL)的推出受到了很多人的欢迎,在 SSL 更新到 3.0 时, 互联网工程任务组(IETF)对 SSL 3.0 进行了标准化,并添加了少数机制,并将其更名为 TLS 1.0(Transport Layer Security 安全传输层协议)。

HTTPS = HTTP + TLS/SSL

在 HTTPS 协议中,当客户端与服务器通过三次握手建立 TCP 连接之后,并不会直接传输数据,而是会经过一个 SSL/TLS 握手的过程,用于协商、以及验证证书等,之后就可以安全传输数据了。

通信建立前:采用非对称加密的方式交换会话密钥,后续不再使用非对称加密。
通信过程中:全部使用对称加密的会话密钥方式,加密明文数据。

TLS/SSL 握手

  1. Client Hello:由客户端发起,主要包含以下信息:

    • 客户端生成的随机数 x,用于之后的密钥生成
    • 客户端支持的加密算法列表(Cipher Suites)
    • TLS 版本信息
    • 客户端支持的压缩算法列表(Compression Methods)
  2. Server Hello:服务端接收到客户端的 Client Hello 之后,服务端需要将自己的 CA 证书发送给客户端,这个步骤叫 Server Certificate。证书是对服务端的一种认证,是由专门的数字证书认证机构(CA)审核之后颁发的,所以一般人无法伪造。在颁发证书的同时还会产生两把钥匙,一把私钥,一把公钥。私钥由服务端保管不可泄露,公钥则附带在证书中公开。证书本身还附带了一个证书电子签名,这个签名用来验证证书的完整性和真实性,防止证书被人窜改。跟客户端一样,服务端也需要生产一个随机数 y 发送给客户端,客户端和服务端都需要使用这俩随机数生成通信密钥,这个过程叫 Server Key Exchange。最后服务端会发送一个 Server Hello Done 给到客户端,表示 Server Hello 过程结束。综上,由服务端发起的消息内容,主要包含:

    • 确认使用的加密通信协议版本,比如 TLS 1.2 版本。如果客户端和服务端支持的版本不一致,服务端关闭加密通信
    • 服务端生成的随机数 y,用于之后的密钥生成
    • 在客户端发送的加密算法列表里选一个加密算法,比如 RSA 公钥加密
    • 服务器 CA 证书
  3. Certificate Verify:如果服务端需要客户端进行验证,在客户端收到服务端的 Server Hello 消息之后,首先需要向服务端发送客户端的证书,让服务端验证客户端的合法性。这个过程叫 Client Certificate。接着,客户端需要对服务端的证书进行检查,如果证书不是可信机构颁布、或证书中的域名与实际域名不符、或者证书已过期,就会向访问者显示一个警告,由其选择是否还要继续通信。如果证书没问题,客户端就会从服务器证书中取出服务端公钥。这个过程叫 Certificate Verify。客户端用这个服务端的公钥加密一个随机数 z,并把这个加密过的随机数发送给服务端,这个过程叫 Client Key Exchange。由客户端会告诉服务端已经切换到协商好的加密算法的状态了,这个过程叫 Change Cipher Spec。最后由已经协商好的加密算法和之前的随机数 x、y、z,产生的一个密钥就是整个消息加密解密过程的核心所在了。这个过程叫 Encrypted Handshake Message。综上,由客户端发送给服务端的信息如下:

    • 由服务器公钥加密过的随机数 z,用于生成服务器的密钥
    • 编码改变通知,表示随后的信息都将由双方协商的加密方法和密钥发送
    • 客户端 TLS 握手结束通知,这一项也是前面发送所有内容的哈希值,用来供服务器校验
  4. Server Finish:服务端在接收到客户端传来的加密过的随机数 z 之后,使用自己的私钥对其进行解密获取随机数 z,并对数据进行验证。验证无误,用协商好的加密算法和之前的随机数 x、y、z 产生服务器密钥,它和客户端生成的密钥是一致的,因为此后的加密都是对称加密了。这个过程叫 Encrypted Handshake Message。等一切完毕之后,会给客户端发送通知,告知客户端已经切换到协商过的加密算法,这个过程叫 Change Cipher Spec。

4 TCP 可靠性保证

4.1 校验和

在 TCP 通信中,发送方会对数据段(包含待传输的数据)计算校验和,并将其添加到数据段的头部。接收方在接收数据段后,同样计算数据的校验和,并与数据段头部中的校验和进行比较。如果两者不一致,就说明数据在传输过程中发生了错误,接收方会请求发送方重新传输数据。

4.2 序列号

在 TCP 通信中,每个传输的数据包都会附带一个序列号,用于标识数据的顺序。接收方会发送确认(ACK)来告知发送方已经收到哪个序列号的数据。如果发送方未收到确认,它会重传相应的数据。

4.3 超时重传

发送方会设置一个定时器,以便在一定时间内等待接收方的确认。如果在该时间内未收到确认,发送方会认为数据包丢失,并重新发送。

4.4 连接管理

TCP 在通信开始时通过三次握手建立连接,并在通信结束时通过四次挥手正确地终止连接,以确保数据的完整传输。

4.5 流量管理

TCP 使用滑动窗口机制来控制发送方和接收方之间的数据流量。接收方通过发送窗口大小告知发送方自己的缓冲区还有多少可用空间,从而控制发送方的发送速率,避免数据的过度拥塞。

4.6 拥塞控制

拥塞控制是为了防止过多的数据注入到网络中,造成网络拥塞。发送端拥有一个拥塞窗口,在发送数据前会对比接收方的滑动窗口大小,取较小值。

拥塞控制有四种算法,慢启动、拥塞避免,快速重传和快速恢复:

  • 慢启动:主机先以比较小的拥塞窗口进行数据发送,由小到大逐渐增加拥塞窗口的大小,这个大小最终是指数增长的。为了防止拥塞窗口 cwnd 增长过快,还要设置一个慢启动阈值,当拥塞窗口的大小超过慢启动阈值时(cwnd > ssthresh),停止使用慢启动算法而改用拥塞避免算法。

  • 拥塞避免:算法的思路是让拥塞窗口 cwnd 缓慢地增大,即每经过一个往返时间 RTT 就把发送方的拥塞窗口 cwnd 加 1,而不是加倍。

  • 快速重传 + 快速恢复:当发送端连续收到三个重复的 ack 时,表示该数据段已经丢失,需要重发。进入快速恢复状态,此时 ssthresh 变为拥塞时 cwnd 的一半,拥塞窗口 cwnd = ssthresh + 3。

  • 普通的恢复方式:当超过设定的时间没有收到某个报文段的 ack 时,表示网络拥塞。此时慢启动阈值 ssthresh 变为 cwnd 的一半,拥塞窗口 cwnd = 1,进入慢启动阶段。


「研」 TCP 连接
https://qanlyma.github.io/Research-Handshake/
作者
Qanly
发布于
2023年6月13日