网络恋爱攻略:传输层连接管理解密,TCP与UDP的浪漫契约!

连接管理

在继续介绍下面有意思的特性之前,我们先来把关注点放在 TCP 的连接管理上,因为没有 TCP 连接,也就没有后续的一系列 TCP 特性什么事儿了。假设运行在一台主机上的进程想要和另一台主机上的进程建立 TCP 连接,会经过如下步骤:

我们假设此时有一台客户端主机和一台服务端主机进行通信。

  1. 首先,客户端首先向服务器发送一个特殊的 TCP 报文段。这个报文段首部不包含数据,但是在报文段的首部中有一个 SYN 标志位被置为 1,这个报文段也可以叫做 SYN 报文段。客户端主机随机选择一个初始序列号(client_isn) ,并将此数字放入初始 TCP SYN 段的序列号字段中发送给服务器。

  2. 一旦此报文到达服务器后,服务器会从报文中提取 TCP SYN 段,将 TCP 缓冲区和变量进行分配,然后给客户端回送一个报文段,这个报文段也不包含任何数据,只做通知的作用。不过它却包含了三个非常重要的信息。

  3. 这些缓冲区和变量的分配使 TCP 容易受到称为 SYN 泛洪的拒绝服务攻击。

  4. 首先,SYN 比特被置为 1 。

  5. 然后,TCP 报文段的首部确认号被设置为 client_isn + 1,也就是 ACK = SYN + 1。

  6. 最后,服务器选择自己的初始序号(server_isn) SYN ,并将其放置到 TCP 报文段首部的序号字段中。

  7. 如果用大白话解释下就是,我收到了你发起建立连接的 SYN 报文段,这个报文段具有首部字段 client_isn。我同意建立该连接,我自己的初始序号是 server_isn。这个允许连接的报文段被称为 SYNACK 报文段。

  8. 第三步,在收到 SYNACK 报文段后,客户端也要为该连接分配缓冲区和变量。客户端向服务器发送另外一个报文段,最后一个报文段对服务器发送的响应报文做了确认,确认的标准是客户端发送的数据段中确认号为 server_isn + 1,因为连接已经建立,所以 SYN 比特被置为 0 。以上就是 TCP 建立连接的三次数据段发送过程,也被称为 三次握手。

一旦完成这三个步骤,客户和服务器主机就可以相互发送报文段了,在以后的每一个报文段中,SYN 比特都被置为 0 ,整个过程描述如下图所示

在客户端主机和服务端主机建立连接后,参与一条 TCP 连接的两个进程中的任何一个都能终止 TCP 连接。连接结束后,主机中的缓存和变量将会被释放。假设客户端主机想要终止 TCP 连接,它会经历如下过程:

客户应用进程发出一个关闭命令,客户 TCP 向服务器进程发送一个特殊的 TCP 报文段,这个特殊的报文段的首部标志 FIN 被设置为 1 。当服务器收到这个报文段后,就会向发送方发送一个确认报文段。然后,服务器发送它自己的终止报文段,FIN 位被设置为 1 。客户端对这个终止报文段进行确认。此时,在两台主机上用于该连接的所有资源都被释放了,如下图所示

在一个 TCP 连接的生命周期内,运行在每台主机中的 TCP 协议都会在各种 TCP 状态(TCP State) 之间进行变化,TCP 的状态主要有 LISTEN、SYN-SEND、SYN-RECEIVED、ESTABLISHED、FIN-WAIT-1、FIN-WAIT-2、CLOSE-WAIT、CLOSING、LAST-ACK、TIME-WAIT 和 CLOSED 。这些状态的解释如下

  1. LISTEN: 表示等待任何来自远程 TCP 和端口的连接请求。

  2. SYN-SEND: 表示发送连接请求后等待匹配的连接请求。

  3. SYN-RECEIVED: 表示已接收并发送连接请求后等待连接确认,也就是 TCP 三次握手中第二步后服务端的状态

  4. ESTABLISHED: 表示已经连接已经建立,可以将应用数据发送给其他主机

上面这四种状态是 TCP 三次握手所涉及的。

  1. FIN-WAIT-1: 表示等待来自远程 TCP 的连接终止请求,或者等待先前发送的连接终止请求的确认。

  2. FIN-WAIT-2: 表示等待来自远程 TCP 的连接终止请求。

  3. CLOSE-WAIT: 表示等待本地用户的连接终止请求。

  4. CLOSING: 表示等待来自远程 TCP 的连接终止请求确认。

  5. LAST-ACK: 表示等待先前发送给远程 TCP 的连接终止请求的确认(包括对它的连接终止请求的确认)。

  6. TIME-WAIT: 表示等待足够的时间以确保远程 TCP 收到其连接终止请求的确认。

  7. CLOSED: 表示连接已经关闭,无连接状态。

上面 7 种状态是 TCP 四次挥手,也就是断开链接所设计的。

TCP 的连接状态会进行各种切换,这些 TCP 连接的切换是根据事件进行的,这些事件由用户调用:OPEN、SEND、RECEIVE、CLOSE、ABORT 和 STATUS 。涉及到 TCP 报文段的标志有 SYN、ACK、RST 和 FIN ,当然,还有超时。

我们下面加上 TCP 连接状态后,再来看一下三次握手和四次挥手的过程。

三次握手建立连接

下图画出了 TCP 连接建立的过程。假设图中左端是客户端主机,右端是服务端主机,一开始,两端都处于CLOSED(关闭)状态。

  1. 服务端进程准备好接收来自外部的 TCP 连接,一般情况下是调用 bind、listen、socket 三个函数完成。这种打开方式被认为是 被动打开(passive open)。然后服务端进程处于 LISTEN 状态,等待客户端连接请求。
  2. 客户端通过 connect 发起主动打开(active open),向服务器发出连接请求,请求中首部同步位 SYN = 1,同时选择一个初始序号 sequence ,简写 seq = x。SYN 报文段不允许携带数据,只消耗一个序号。此时,客户端进入 SYN-SEND 状态。
  3. 服务器收到客户端连接后,,需要确认客户端的报文段。在确认报文段中,把 SYN 和 ACK 位都置为 1 。确认号是 ack = x + 1,同时也为自己选择一个初始序号 seq = y。请注意,这个报文段也不能携带数据,但同样要消耗掉一个序号。此时,TCP 服务器进入 SYN-RECEIVED(同步收到) 状态。
  4. 客户端在收到服务器发出的响应后,还需要给出确认连接。确认连接中的 ACK 置为 1 ,序号为 seq = x + 1,确认号为 ack = y + 1。TCP 规定,这个报文段可以携带数据也可以不携带数据,如果不携带数据,那么下一个数据报文段的序号仍是 seq = x + 1。这时,客户端进入 ESTABLISHED (已连接) 状态
  5. 服务器收到客户的确认后,也进入 ESTABLISHED 状态。

TCP 建立一个连接需要三个报文段,释放一个连接却需要四个报文段。

四次挥手

数据传输结束后,通信的双方可以释放连接。数据传输结束后的客户端主机和服务端主机都处于 ESTABLISHED 状态,然后进入释放连接的过程。

TCP 断开连接需要历经的过程如下:

  1. 客户端应用程序发出释放连接的报文段,并停止发送数据,主动关闭 TCP 连接。客户端主机发送释放连接的报文段,报文段中首部 FIN 位置为 1 ,不包含数据,序列号位 seq = u,此时客户端主机进入 FIN-WAIT-1(终止等待 1) 阶段。
  2. 服务器主机接受到客户端发出的报文段后,即发出确认应答报文,确认应答报文中 ACK = 1,生成自己的序号位 seq = v,ack = u + 1,然后服务器主机就进入 CLOSE-WAIT(关闭等待) 状态,这个时候客户端主机 -> 服务器主机这条方向的连接就释放了,客户端主机没有数据需要发送,此时服务器主机是一种半连接的状态,但是服务器主机仍然可以发送数据。
  3. 客户端主机收到服务端主机的确认应答后,即进入 FIN-WAIT-2(终止等待2) 的状态。等待客户端发出连接释放的报文段。
  4. 当服务器主机没有数据发送后,应用进程就会通知 TCP 释放连接。这时服务端主机会发出断开连接的报文段,报文段中 ACK = 1,序列号 seq = w,因为在这之间可能已经发送了一些数据,所以 seq 不一定等于 v + 1。ack = u + 1,在发送完断开请求的报文后,服务端主机就进入了 LAST-ACK(最后确认)的阶段。
  5. 客户端收到服务端的断开连接请求后,客户端需要作出响应,客户端发出断开连接的报文段,在报文段中,ACK = 1, 序列号 seq = u + 1,因为客户端从连接开始断开后就没有再发送数据,ack = w + 1,然后进入到 TIME-WAIT(时间等待) 状态,请注意,这个时候 TCP 连接还没有释放。必须经过时间等待的设置,也就是 2MSL 后,客户端才会进入 CLOSED 状态,时间 MSL 叫做最长报文段寿命(Maximum Segment Lifetime)
  6. 服务端主要收到了客户端的断开连接确认后,就会进入 CLOSED 状态。因为服务端结束 TCP 连接时间要比客户端早,而整个连接断开过程需要发送四个报文段,因此释放连接的过程也被称为四次挥手。

TCP 连接的任意一方都可以发起关闭操作,只不过通常情况下发起关闭连接操作一般都是客户端。然而,一些服务器比如 Web 服务器在对请求作出相应后也会发起关闭连接的操作。TCP 协议规定通过发送一个 FIN 报文来发起关闭操作。

所以综上所述,建立一个 TCP 连接需要三个报文段,而关闭一个 TCP 连接需要四个报文段。TCP 协议还支持一种半开启(half-open) 状态,虽然这种情况并不多见。

TCP 半开启

TCP 连接处于半开启的这种状态是因为连接的一方关闭或者终止了这个 TCP 连接却没有通知另一方,也就是说两个人正在微信聊天,cxuan 你下线了你不告诉我,我还在跟你侃八卦呢。此时就认为这条连接处于半开启状态。这种情况发生在通信中的一方处于主机崩溃的情况下,人家发送方还是有理由说的啊,你 xxx 的,我电脑死机了我咋告诉你?只要处于半连接状态的一方不传输数据的话,那么是无法检测出来对方主机已经下线的。

另外一种处于半开启状态的原因是通信的一方关闭了主机电源 而不是正常关机。这种情况下会导致服务器上有很多半开启的 TCP 连接。

TCP 半关闭

既然 TCP 支持半开启操作,那么我们可以设想 TCP 也支持半关闭操作。同样的,TCP 半关闭也并不常见。TCP 的半关闭操作是指仅仅关闭数据流的一个传输方向 。两个半关闭操作合在一起就能够关闭整个连接。在一般情况下,通信双方会通过应用程序互相发送 FIN 报文段来结束连接,但是在 TCP 半关闭的情况下,应用程序会表明自己的想法:"我已经完成了数据的发送发送,并发送了一个 FIN 报文段给对方,但是我依然希望接收来自对方的数据直到它发送一个 FIN 报文段给我"。 下面是一个 TCP 半关闭的示意图。

解释一下这个过程:

首先客户端主机和服务器主机一直在进行数据传输,一段时间后,客户端发起了 FIN 报文,要求主动断开连接,服务器收到 FIN 后,回应 ACK ,由于此时发起半关闭的一方也就是客户端仍然希望服务器发送数据,所以服务器会继续发送数据,一段时间后服务器发送另外一条 FIN 报文,在客户端收到 FIN 报文回应 ACK 给服务器后,断开连接。

TCP 的半关闭操作中,连接的一个方向被关闭,而另一个方向仍在传输数据直到它被关闭为止。只不过很少有应用程序使用这一特性。

同时打开和同时关闭

还有一种比较非常规的操作,这就是两个应用程序同时主动打开连接。虽然这种情况看起来不太可能,但是在特定的安排下却是有可能发生的。我们主要讲述这个过程。

通信双方在接收到来自对方的 SYN 之前会首先发送一个 SYN,这个场景还要求通信双方都知道对方的 IP 地址 + 端口号

下面是同时打开的例子

如上图所示,通信双方都在收到对方报文前主动发送了 SYN 报文,都在收到彼此的报文后回复了一个 ACK 报文。

一个同时打开过程需要交换四个报文段,比普通的三次握手增加了一个,由于同时打开没有客户端和服务器一说,所以这里我用了通信双方来称呼。

像同时打开一样,同时关闭也是通信双方同时提出主动关闭请求,发送 FIN 报文,下图显示了一个同时关闭的过程。

同时关闭过程中需要交换和正常关闭相同数量的报文段,只不过同时关闭不像四次挥手那样顺序进行,而是交叉进行的。

聊一聊初始序列号

也许是我上面图示或者文字描述的不专业,初始序列号它是有专业术语表示的,初始序列号的英文名称是Initial sequence numbers (ISN) ,所以我们上面表示的 seq = v,其实就表示的 ISN。

在发送 SYN 之前,通信双方会选择一个初始序列号。初始序列号是随机生成的,每一个 TCP 连接都会有一个不同的初始序列号。RFC 文档指出初始序列号是一个 32 位的计数器,每 4 us(微秒) + 1。因为每个 TCP 连接都是一个不同的实例,这么安排的目的就是为了防止出现序列号重叠的情况。

当一个 TCP 连接建立的过程中,只有正确的 TCP 四元组和正确的序列号才会被对方接收。这也反应了 TCP 报文段容易被伪造 的脆弱性,因为只要我伪造了一个相同的四元组和初始序列号就能够伪造 TCP 连接,从而打断 TCP 的正常连接,所以抵御这种攻击的一种方式就是使用初始序列号,另外一种方法就是加密序列号。

什么是 TIME-WAIT

我上面只是简单提到了一下 TIME-WAIT 状态和 2MSL 是啥,下面来聊一下这两个概念。

MSL 是 TCP 报文段可以存活或者驻留在网络中的最长时间。RFC 793 定义了 MSL 的时间是两分钟,但是具体的实现还要根据程序员来指定,一些实现采用了 30 秒的这个最大存活时间。

那么为什么要等待 2MSL 呢?

主要是因为两个理由

  1. 为了保证最后一个响应能够到达服务器,因为在计算机网络中,最后一个 ACK 报文段可能会丢失,从而致使客户端一直处于 LAST-ACK 状态等待客户端响应。这时候服务器会重传一次 FINACK 断开连接报文,客户端接收后再重新确认,重启定时器。如果客户端不是 2MSL ,在客户端发送 ACK 后直接关闭的话,如果报文丢失,那么双方主机会无法进入 CLOSED 状态。

  2. 还可以防止已失效的报文段。客户端在发送最后一个 ACK 之后,再经过经过 2MSL,就可以使本链接持续时间内所产生的所有报文段都从网络中消失。从保证在关闭连接后不会有还在网络中滞留的报文段去骚扰服务器。

这里注意一点:在服务器发送了 FIN-ACK 之后,会立即启动超时重传计时器。客户端在发送最后一个 ACK 之后会立即启动时间等待计时器。

说好的 RST 呢

说好的 RST、SYN、FIN 标志用于连接的建立和关闭,那么 SYN 和 FIN 都现身了,那 RST 呢?也是啊,我们上面探讨的都是一种理想的情况,就是客户端服务器双方都会接受传输报文段的情况,还有一种情况是当主机收到 TCP 报文段后,其 IP 和端口号不匹配的情况。假设客户端主机发送一个请求,而服务器主机经过 IP 和端口号的判断后发现不是给这个服务器的,那么服务器就会发出一个 RST 特殊报文段给客户端。

因此,当服务端发送一个 RST 特殊报文段给客户端的时候,它就会告诉客户端没有匹配的套接字连接,请不要再继续发送了

上面探讨的是 TCP 的情况,那么 UDP 呢?

使用 UDP 作为传输协议后,如果套接字不匹配的话,UDP 主机就会发送一个特殊的 ICMP 数据报。

SYN 洪泛攻击

下面我们来讨论一下什么是 SYN 洪泛攻击

我们在 TCP 的三次握手中已经看到,服务器为了响应一个收到的 SYN,分配并初始化变量连接和缓存,然后服务器发送一个 SYNACK 作为响应,然后等待来自于客户端的 ACK 报文。如果客户端不发送 ACK 来完成最后一步的话,那么这个连接就处在一个挂起的状态,也就是半连接状态。

攻击者通常在这种情况下发送大量的 TCP SYN 报文段,服务端继续响应,但是每个连接都完不成三次握手的步骤。随着 SYN 的不断增加,服务器会不断的为这些半开连接分配资源,导致服务器的连接最终被消耗殆尽。这种攻击也是属于 Dos 攻击的一种。

抵御这种攻击的方式是使用 SYN cookie ,下面是它的工作流程介绍

  1. 当服务器收到一个 SYN 报文段时,它并不知道这个报文段是来自哪里,是来自攻击者主机还是客户端主机(虽然攻击者也是客户端,不过这么说更便于区分) 。因此服务器不会为报文段生成一个半开连接。与此相反,服务器生成一个初始的 TCP 序列号,这个序列号是 SYN 报文段的源和目的 IP 地址与端口号这个四元组构造的一个复杂的散列函数,这个散列函数生成的 TCP 序列号就是 SYN Cookie,用于缓存 SYN 请求。然后,服务器会发送带着 SYN Cookie 的 SYNACK 分组。有一点需要注意的是,服务器不会记忆这个 Cookie 或 SYN 的其他状态信息
  2. 如果客户端不是攻击者的话,它就会返回一个 ACK 报文段。当服务器收到这个 ACK 后,需要验证这个 ACK 与 SYN 发送的是否相同,验证的标准就是确认字段中的确认号和序列号,源和目的 IP 地址与端口号以及和散列函数的是否一致,散列函数的结果 + 1 是否和 SYNACK 中的确认值相同。(大致是这样,说的不对还请读者纠正) 。如果有兴趣读者可以自行深入了解。如果是合法的,服务器就会生成一个具有套接字的全开连接。
  3. 如果客户端没有返回 ACK,即认为是攻击者,那么这样也没关系,服务器没有收到 ACK,不会分配变量和缓存资源,不会对服务器产生危害。
相关推荐
VVVVWeiYee4 小时前
项目2路由交换
运维·服务器·网络·网络协议·信息与通信
手心里的白日梦9 小时前
UDP传输层通信协议详解
网络·网络协议·udp
红米饭配南瓜汤10 小时前
WebRTC服务质量(11)- Pacer机制(03) IntervalBudget
网络·网络协议·音视频·webrtc·媒体
萧瑟其中~12 小时前
计算机网络:TCP/IP网络协议
网络协议·tcp/ip·计算机网络
哈利巴多先生16 小时前
HTTP,续~
网络·网络协议·http
白了个白i16 小时前
http的访问过程或者访问页面会发生什么
网络·网络协议·http
qq_3720068617 小时前
浏览器http缓存问题
网络协议·http·缓存
科技小E19 小时前
国标GB28181设备管理软件EasyGBS:P2P远程访问故障排查指南(设备端)
网络协议·智能路由器·音视频·p2p
hgdlip20 小时前
手机IP地址:定义、查看与切换方法
网络协议·tcp/ip·智能手机
dengjiayue1 天前
tcp 的重传,流量控制,拥塞控制
网络协议·tcp/ip