传输层

TCP 特点

TCP 是面向连接的、可靠的、基于字节流的:

  • 面向连接的:指的是一对一的连接方式、不能像 UDP 那样可以一个主机同时向多个主机发送消息,也就是说一对多是无法实现的
  • 可靠的:无论网络状况出现了怎样的变化,TCP 都能保证报文一定能够到达接收端
  • 字节流:UDP 的数据传输是基于数据报的,这是因为仅仅只是继承了 IP 层的特性,而 TCP 为了维护状态,将一个个 IP 包变成了字节流

TCP 与 UDP 的区别

  • 连接方面
    • TCP 是面向连接的传输层协议,传输之前首先要建立连接
    • UDP 不需要建立连接即可传输数据
  • 服务对象
    • TCP 是一对一的两点服务,即一条连接只有两个端点
    • UDP 支持一对一、一对多、多对多的交通通信
  • 可靠性
    • TCP 是可靠交付数据的,数据可以无差别、不丢失、不重复、按需到达
    • UDP 只尽最大可能交付,不保证可靠交付数据
  • 拥塞控制、流量控制
    • TCP 有拥塞控制和流量控制机制,考证数据传输的安全性
    • UDP 则没有,即使网络非常拥堵了,也不会影响 UDP 的发送速率,所以 UDP 具有低延迟的特性
  • 首部开销
    • TCP 首部长度较长,会有一定的开销,首部在没有使用【选项】字段时有 20 个字节,如果使用了【选项】字段会更长,最大可达 60 个字节
    • UDP首部只有8个字节,并且是固定不变的、开销较小
  • 传输方式
    • TCP 是流式传输、没有边界,但保证顺序和可靠

TCP 报文

TCP 报文结构

TCP 报文

如何标识唯一标识一个连接?答案是 TCP 连接的四元组 —— 源 IP、源端口、目标 IP 和目标端口。那 TCP 报文怎么没有源 IP 和目标 IP 呢?这是因为在 IP 层就已经处理了 IP ,所以 TCP 只需要记录两者的端口即可。

序列号 Sequence number

序列号在 TCP 通信的过程中有 3 个作用:

  • 接收方可以去除重复的数据
  • 接收方可以根据数据包的序列号按照顺序接收
  • 可以表示发出去的数据包中哪些已经被接收(SACK)

初始序列号 ISN

即 Initial Sequence Number

确认应答号 Ack Num

标记位

常见的标记为包括 SYN、ACK、FIN、RST

三次握手

三次握手过程

三次握手

一开始客户端和服务端都处于 CLOSED 状态,然后服务端主动监听了某个端口,处于 LISTEN 状态。接着开始正式的三次握手过程:

  • 客户端随机初始化一个序号 client_isn,将序号填入报文的序号字段。同时把 SYN 标志为 1,表示 SYN 报文。接着把第一个 SYN 报文发送给服务端,表示向服务端发起连接,之后客户端处于 SYN-SENT 状态

第一次握手

  • 服务端接收到 SYN 报文后,首先服务端也随机初始化自己的序号 server_isn,将序号填入报文的序号字段。其次把 TCP 首部的确认应答号填入 client_isn + 1,接着把 SYN 和 ACK 标志为 1。最后将报文发给客户端。该报文也不包含应用层数据。之后服务端处于 SYN-RCVD 状态

第二次握手

  • 客户端收到服务端报文后,还要向服务端回应最后一个报文,首先报文 TCP 首部的 ACK 标志为 1,其次确认应答号填入 server_isn + 1,最后把报文发送给服务端。这次报文可以携带客户端到服务端的数据。之后客户端处于 ESTABLISHED 状态

第三次握手

  • 服务端收到客户端的报文之后,也进入 ESTABLISHED 状态。

从上面的过程可以发现第三次握手是可以携带数据的,而前两次握手是不可以携带数据的。

为什么不是两次握手?

  • 首要目的是防止旧的连接导致数据错乱。⽹络环境是很复杂的,往往并不是如我们期望的⼀样,先发送的数据包,不一定先到达⽬标主机,反而可能会由于⽹络拥堵等乱七八糟的原因使得旧的数据包,先到达目标主机。如果一个旧的 SYN 报文比新的 SYN 更早到达了服务端的话,服务端会返回一个 SYN + ACK 报文给客户端。客户端收到之后可以根据确认应答号判断这是一个历史连接,然后会发送一个 RST 报文给服务端,表示终止这次连接;反之,如果这是最新的报文,就返回一个 ACK 报文告知服务端。如果是两次握手,那客户端即便知道这是一个历史连接,也无法告知服务端
  • 第二个目的是同步双方的初始化序列号。当客户端发送携带初始序列号的 SYN 报文的时候,需要服务端回复一个 ACK 报文,表示客户端的 SYN 报文已经被服务端成功接收;所以当服务端发送初始序列号给客户端的时候,也要得到客户端的应答。这样一来一回才能确保双方的初始序列号能被同步。两次握手只保证了一方的初始序列号能被对方成功接收,没办法保证双方的初始序列号都能被确认接收。序列号的作用有:
    • 接收方可以去除重复的数据
    • 接收方可以根据数据包的序列号按照顺序接收
    • 可以表示发出去的数据包中哪些已经被接收
  • 第三个目的是防止资源浪费。如果只有两次握手,当客户端的 SYN 请求连接在网络中阻塞,客户端没有接收到 ACK 报⽂,就会重新发送 SYN ,由于没有第三次握⼿,服务器不清楚客户端是否收到了自己发送的建⽴连接的 ACK 确认信号,所以每次收到一个 SYN 就会建立一个连接,这样就会存在很多冗余的链接

为什么不是四次握手?

四次握手

三次握手理论上已经足够可靠,中间两步可以合成一步。所以不需要使用更多的通信次数。

为什么客户端和服务端的初始序列号 ISN 每次连接都要重新生成?

  • 假设⼀个已经失效的连接被重用了,但是该旧连接的历史报⽂还残留在网络中,如果序列号相同,那么就无法分辨出该报文是不是历史报文,如果历史报文被新的连接接收了,则会产生数据错乱
  • 另一方面是为了安全性,防止黑客伪造相同序列号的 TCP 报文

ISN 是如何生成的?

ISN 并不是一个固定的值,而是每 4 ms 加一,溢出则回到 0,这个算法使得猜测 ISN 变得很困难。那为什么要这么做?如果 ISN 被攻击者预测到,要知道源 IP 和源端口号都是很容易伪造的,当攻击者猜测 ISN 之后,直接伪造一个 RST 后,就可以强制连接关闭的,这是非常危险的。而动态增长的 ISN 大大提高了猜测 ISN 的难度。

SYN Flood 攻击

洪水攻击属于典型的 DDoS 攻击。其攻击的原理很简单,就是用客户端在短时间内伪造大量不存在的 IP 地址,并向服务端疯狂发送SYN。对于服务端而言,会产生两个危险的后果:

  1. 处理大量的 SYN 包并返回对应 ACK,势必有大量连接处于 SYN_RCVD 状态,从而占满整个半连接队列,无法处理正常的请求
  2. 由于是不存在的 IP,服务端长时间收不到客户端的ACK,会导致服务端不断重发数据,直到耗尽服务端的资源

四次挥手

©2022 Flower-F. All Rights Reserved.
Built based on Takuya Matsuyama's website.