计算机网络 (13) 运输层-TCP连接建立与释放

79 阅读14分钟

本文引用图片均来自 高军: 计算机网络

TCP作为面向连接的协议,在每次通信前必须建立连接,通信结束后必须释放连接

建立连接

TCP建立连接主要是为了确认对方的存在、双方的收发能力以及协商参数

一般的,将主动发起TCP连接的进程称为客户进程,另一方则称为服务进程。初始时服务进程处于监听状态,等待客户进程的连接建立请求

当客户进程想要建立连接时就发送TCP请求报文段,并在发送报文后进入同步已发送状态。报文段首部中同步位SYN值为1,表明这是TCP连接请求报文段。序号字段seq为一个随机值x,作为客户进程为此次通信选择的初始序号。值得注意的是SYN为1的报文段不能携带数据

image.png

服务进程收到请求后返回TCP连接请求确认报文段,并在发送报文后进入同步已接收状态。报文段首部中同步位SYN确认位ACK值都为1,表明这是TCP连接请求确认报文段。序号字段seq为一个随机值y,作为服务进程为此次通信选择的初始序号确认字段ack值为x+1,表明对客户进程所选择的初始序号值x的确认

image.png

客户进程收到TCP连接请求确认报文段后需要向服务进程发送一个普通TCP确认报文段,并在发送报文后进入连接已建立状态。报文段首部中确认位ACK值为1,表明这是普通TCP确认报文段。序号字段seqx+1,这是因为客户进程在请求建立连接时选择的初始序号为x。确认字段ack值为y+1,表明对服务进程所选择的初始序号值y的确认。值得注意的是普通ACK确认报文段可以携带数据,但如果不携带数据则不消耗序号,这样发送的下一个报文段序号仍为x+1

image.png

服务进程收到确认报文段后也进入连接已建立状态,至此,双方的TCP连接建立成功可以进行通信了

建立连接的流程主要分为三步,因此也被称为三次握手,下面是一些握手过程中的要点:

能否只握手两次?

答案是不行的

首先从确认双方收发能力的角度来看:服务进程收到客户进程的连接请求报文,知道客户进程发送能力。客户进程收到服务进程的请求确认报文,知道服务进程接收和发送能力。如果此时客户进程不返回普通确认报文,那么服务进程就无法确认客户进程是否收到请求确认报文,不知道客户进程有没有接收能力。本人认为以上说法适用于这样一种情形——在两次握手建立连接后,客户进程并没有发送数据,服务进程想发送数据却因为不确定客户进程的接收能力而无法发送

然后从资源的角度来看:客户进程发送的连接请求报文可能在网络上长时间滞留,那么客户进程会因为长时间没收到服务进程的请求确认报文而重传请求报文。假设重传请求报文后成功建立了连接并且双方基于连接完成了通信,那么客户进程就处于连接关闭状态。如果此时在网络上滞留的请求报文到达了服务进程,那么服务进程会认为有客户进程发起了新的请求,此时服务进程返回请求确认报文并进入连接已建立状态,等待客户进程发送数据。但是客户进程并没有发起新的请求所以它会忽略请求确认报文,而此时服务进程并不知情而一直等待。

image.png

三次握手出现丢包怎么办?

丢包主要分为以下三种情况:

  1. 客户进程连接请求报文丢失:报文丢失后客户进程会因为迟迟收不到服务进程的响应报文而触发超时重传。注意,重传次数是有限的,每次重传等待时间不同
  2. 服务进程请求确认报文丢失:报文丢失后服务进程会因为迟迟收不到客户进程的响应报文而触发超时重传。同样的,服务进程重传次数也是有限的且每次重传等待时间不同。另外,此时客户进程会因为收不到确认报文而触发超时重传
  3. 客户进程普通确认报文丢失:此时,客户进程已经单方面进入连接已建立状态并且可以发送数据了。那么普通确认报文丢失就要分为以下几种情况:a) 客户进程和服务进程都没有数据要发送,此时服务进程会因为迟迟收不到客户进程的响应而触发超时重传,重传次数有限。b) 客户进程发送了数据,此时服务进程根据报文中的ack字段可以知道客户进程已经收到了请求确认报文,服务进程进入连接已建立状态并接受数据包。c) 服务进程想发送数据却发送不了,触发超时重传,重传次数有限。以上a和c中如果服务进程重传次数用尽仍未收到客户进程的响应且客户进程发送数据到服务进程,此时服务进程会返回RST(Reset the connection)响应关闭连接

为什么初始序号是随机的?

初始序号ISN(Initial Sequence Number)是双方协商的主要参数之一,用于让对方知道接下来接收数据的时候如何按序号组装数据,详情可参考这里

ISN随机主要有两个方面的考虑:

  1. 安全性:如果ISN可预测,那么恶意者可以伪装IP向服务器发送连接请求,随后恶意者使用预测的服务器ISN向服务器再发送一个普通确认报文,那么服务器就会和伪装过的恶意者建立起连接。这样,无论谁都可以伪装任意IP和服务器建立连接
  2. 数据可靠性:客户进程和服务进程可能会频繁的建立和断开连接,假设每次建立连接都使用固定的ISN,那么可能出现下述情况:某次通信过程中的数据包在网络上长时间滞留,此次连接结束前仍未到达服务进程。客户进程后来和服务进程建立了新的连接,此时滞留的数据包到达了服务进程且携带的序号刚好和服务进程在这次新的连接中期待的序号相同,那么服务器就接受了旧的数据导致数据错误。详情可参考这里

RFC793提到ISN随机算法:ISN = M + F(localhost, localport, remotehost, remoteport)M是计时器每隔4毫秒加1, F是hash算法

为什么第一二次握手不能携带数据?

首先这是协议的规定,在前两次握手中报文首部的同步位SYN置为表示本次数据报不能携带数据。然后,从连接建立的角度来看,此时连接尚未建立,服务进程和客户进行都不知道对方的接收能力,不能携带数据也是理所当然的。最后,从安全的角度来看,如果前两次握手能携带数据,那么攻击者可以在第一次握手时携带大量数据然后大量发送连接建立请求从而消耗服务器大量资源

第三次握手可以携带数据是因为客户进程收到服务进程的请求确认报文后就已经知道服务进程的接收和发送能力,此时客户进程就可以进入连接已建立状态,携带数据也是理所当然的

在这里额外提一下握手过程中的SYN攻击

SYN攻击

攻击者发出连接建立请求报文(SYN报文)并在收到服务进程的请求确认报文后不回复任何报文(不执行第三次握手),此时的TCP连接处于半连接状态,服务进程会把处于半连接状态的连接放入半连接队列中。由于迟迟收不到客户进程的普通请求确认报文,服务进程会一次次的触发超时重传。虽然重传有次数限制,但这也会耗费一定的时间。如果服务进程短时间收到大量同样的恶意连接请求就会耗费服务器大量资源

释放连接

释放连接的请求一般由客户进程主动发起

当客户进程想要释放连接时就发送TCP连接释放报文段,并在发送报文后进入终止等待1状态。报文段首部中终止位FIN确认位ACK值为1,表明这是TCP连接释放报文段。序号字段sequ等于客户进程之前发送过的数据最后一个字节的序号加1。确认号字段ack的值v等于客户进程之前收到过的数据最后一个字节的序号加1。值得注意的是FIN为1的报文即使不携带数据也要消耗一个序号

image.png

服务进程收到连接释放报文段后返回普通TCP确认报文段,并在发送报文后进入关闭等待状态。报文段首部中确认位ACK值为1,表明这是普通TCP确认报文段。序号字段seq的值v等于服务进程之前已发送过的数据最后一个字节的序号加1。确认号字段ack值为u+1,表明对客户进程连接释放报文的确认

此时的TCP连接处于半关闭状态,客户进程不能再发送数据但是可以接收数据,服务进程还可以发送数据

image.png

客户进程收到普通TCP确认报文段后便进入终止等待2状态,等待服务进程关闭连接。当服务进程数据传输完毕就向客户进程发送连接释放报文,并在发送报文后进入最后确认状态。报文段首部中终止位FIN确认位ACK值为1,表明这时TCP连接释放报文段。序号字段seqw等于服务进程已发送过的数据最后一个字节的序号加1。确认号字段acku+1,表明对客户进程连接释放报文的重复确认

image.png

客户进程收到连接释放报文段后返回普通TCP确认报文段,并在发送报文后进入时间等待状态。报文段首部中确认位ACK值为1,表面这是普通TCP确认报文段。序号字段sequ+1确认字段ackw+1,表明对服务进程连接释放报文的确认

image.png

服务进程收到确认报文后便进入连接关闭状态,客户进程则需要等待两倍报文段最大生存时间MSL((Maximum Segment Lifetime)才能进入关闭状态

释放连接的流程主要分为四步,因此也被称为四次挥手,下面是一些挥手过程中的要点:

为什么客户端需要等待2倍MSL时间?

MSL是任何报文能在网络上存活的最长时间,超过时间的报文将被网络丢弃

首先从丢包的角度看,如果最后一次挥手的报文段丢失,服务进程会因为迟迟收不到确认报文而触发超时重传,但是此时客户进程已经进入关闭状态,重传的连接释放报文会被直接丢弃,这样服务进程将一直收不到客户进程对服务进程连接释放的确认,从而造成服务进程等待直到重传次数耗尽

等待2MSL时间可以确保客户进程以较大的概率能收到服务进程重传的连接释放报文,当客户端收到重传的连接释放报文后计时器会重新计时

然后是数据的可靠性角度,等待2MSL时间可以确保本次连接产生的报文都从网络中消失,防止有些旧报文滞留在网络上干扰新连接的数据

RFC793建议MSL为两分钟,但是时间等待状态需要耗费CPU和占用端口等资源,如果出现过多处于时间等待状态的连接可能耗费大量的CPU资源以及无法发起新的连接,所以MSL一般都根据不同的网络情况会进行调整

四次挥手出现丢包怎么办?

挥手过程中出现丢包可以参考握手过程的丢包处理,主要都是触发超时重传,重传都有次数限制超过之后就进入关闭状态,需要注意的是普通TCP确认报文(ACK报文)是不重传的,只有连接释放报文(FIN报文)会重传

为什么需要挥手四次?

通俗的讲就是一方没有数据需要发送想关闭连接了,另一方给予确认,这里产生两次挥手。但是,另一方可能还需要发送数据,等另一方也没数据需要发送了这里又产生一次关闭和确认的过程,产生两次挥手。以上过程加起来就一共四次挥手

如果服务进程在收到客户进程的连接释放请求后也没有数据需要发送了,那么能不能把确认报文和连接释放报文合并(二、三次挥手合并)呢?可以的话就只需要三次挥手

保活计时器是什么?

建立连接后一方如果故障即不发生数据又不关闭连接会导致另一方一直等待,为了避免这种情况就需要设置一个计时器,如果在一定时限内没有收到另一方的任何报文,此时会发送探测报文看看对方是否正常。若连续10个探测报文都无响应,那么就可以判断另一方故障直接关闭连接。当然,每次收到数据保活计时器都会重置

客户进程在收到服务进程的连接释放报文后又收到服务进程的数据报会怎样?

这个问题就是说服务进程的数据在网络上滞留了,这些滞留的数据在服务进程发送了连接释放报文后才到达客户进程

终止等待2状态的客户进程收到连接释放报文后通过检查首部的序号字段可以发现该报文是乱序报文,此时客户进程会把报文放入乱序队列中并且不会进入时间等待状态。等客户进程收到滞留的数据时会检查乱序队列看能不能找到相应序号的数据,如果找到了还会查看其首部的终止位FIN,发现其值为1才会进入时间等待状态。详情可参考这里

参考文献