计算机网络(TCP篇)

发布于:2025-08-04 ⋅ 阅读:(11) ⋅ 点赞:(0)

TCP 和 UDP

TCP 基本认识

TCP 头格式

在这里插入图片描述

TCP

TCP 是面向连接的、可靠的、基于字节流的传输层通信协议。

在这里插入图片描述

TCP 是一个工作在传输层可靠数据传输的服务,它能确保接收端接收的网络包是无损坏、无间隔、非冗余和按序的

TCP 连接

连接定义

在这里插入图片描述

TCP 四元组

TCP 四元组可以唯一的确定一个连接。

在这里插入图片描述

在这里插入图片描述

最大 TCP 连接数

在这里插入图片描述

对 IPv4,

  • 客户端的 IP 数最多为 2 的 32 次方
  • 客户端的端口数最多为 2 的 16 次方

服务端单机最大 TCP 连接数约为 2 的 48 次方。

服务端最大并发 TCP 连接数远不能达到理论上限,受文件描述符限制(系统级、用户级、进程级)、文件描述符限制因素影响。

TCP缺陷

在这里插入图片描述

  • TCP 协议是在内核中实现的,应用程序只能使用不能修改,如果要想升级 TCP 协议,那么只能升级内核。

    内核升级涉及到底层软件和运行库的更新,服务程序就需要回归测试是否兼容新的内核版本,所以服务器的内核升级比较保守和缓慢

  • 基于 TCP 实现的应用协议,都是需要先建立三次握手才能进行数据传输,比如 HTTP 1.0/1.1、HTTP/2、HTTPS。

    • TCP 三次握手的延迟被 TCP Fast Open (快速打开)这个特性解决了,这个特性可以在「第二次建立连接」时减少 TCP 连接建立的时延。
    • TCP Fast Open 需要服务端和客户端的操作系统同时支持才能体验到,很难被普及开来。
      在这里插入图片描述
  • 现在大多数网站都是使用 HTTPS 的,这意味着在 TCP 三次握手之后,还需要经过 TLS 四次握手后,才能进行 HTTP 数据的传输,这在一定程序上增加了数据传输的延迟。

    针对 HTTPS 来说,TLS 是在应用层实现的握手,而 TCP 是在内核实现的握手,这两个握手过程是无法结合在一起的,总是得先完成 TCP 握手,才能进行 TLS 握手

在这里插入图片描述

TCP 队头阻塞问题

  • TCP 是字节流协议,TCP 层必须保证收到的字节数据是完整有序的。
  • TCP 队头阻塞的问题,其实就是接收窗口的队头阻塞问题
    在这里插入图片描述

在这里插入图片描述

eg. 当接收窗口收到的数据不是有序的,比如收到第 33~40 字节的数据,由于第 32 字节数据没有收到, 接收窗口无法向前滑动,那么即使先收到第 33~40 字节的数据,这些数据也无法被应用层读取的。
在这里插入图片描述

UDP 头部格式

在这里插入图片描述

TCP 和 UDP 区别

在这里插入图片描述
在这里插入图片描述

应用场景

在这里插入图片描述

使用同一个端口

TCP & UDP(可以)

  • 传输层的「端口号」的作用,是为了区分同一个主机上不同应用程序的数据包。
  • 传输层有两个传输协议分别是 TCP 和 UDP,在内核中是两个完全独立的软件模块。
    在这里插入图片描述

多个 TCP 服务进程(不同IP可以)

  • 如果两个 TCP 服务进程同时绑定的 IP 地址和端口都相同,那么执行 bind() 时候就会出错,错误是“Address already in use”。

在这里插入图片描述

  • 因为只要客户端连接的服务器不同,端口资源可以重复使用的。

客户端端口选择的流程

在这里插入图片描述

MTU & MSS

在这里插入图片描述

在这里插入图片描述

  • IP 层进行分片传输,是非常没有效率的。

    • 如果一个 IP 分片丢失,整个 IP 报文的所有分片都得重传。
    • IP 层本身没有超时重传机制,它由传输层的 TCP 来负责超时和重传。
  • 为了达到最佳的传输效能 TCP 协议在建立连接的时候通常要协商双方的 MSS 值

    • 当 TCP 层发现数据超过 MSS 时,则就先会进行分片,当然由它形成的 IP 包的长度也就不会大于 MTU ,自然也就不用 IP 分片了。
    • 经过 TCP 层分片后,如果一个 TCP 分片丢失后,进行重发时也是以 MSS 为单位,而不用重传所有的分片,大大增加了重传的效率。

MSL

MSL 是 Maximum Segment Lifetime,报文最大生存时间,超过这个时间报文将被丢弃。

MSL 与 TTL 的区别

  • MSL 的单位是时间,而 TTL 是经过路由跳数。
  • MSL 应该要大于等于 TTL 消耗为 0 的时间,以确保报文已被自然消亡。

TTL 的值一般是 64,Linux 将 MSL 设置为 30 秒,意味着 Linux 认为数据报文经过 64 个路由器的时间不会超过 30 秒,如果超过了,就认为报文已经消失在网络中了。

TIME_WAIT 等待的时间是 2MSL

网络中可能存在来自发送方的数据包,当这些发送方的数据包被接收方处理后又会向对方发送响应,所以一来一回需要等待 2 倍的时间

QUIC 协议(基于UDP协议实现可靠传输)

Packet Header

重要的字段
在这里插入图片描述
QUIC 也是需要三次握手来建立连接的,主要目的是为了协商连接 ID

Packet Number

Packet Number 是每个报文独一无二的编号,它是严格递增的。

在这里插入图片描述

QUIC 使用的 Packet Number 单调递增的设计,可以让数据包不再像 TCP 那样必须有序确认,QUIC 支持乱序确认,当数据包Packet N 丢失后,只要有新的已接收数据包确认,当前窗口就会继续向右滑动。

QUIC Frame Header

  • 引入 Frame Header 这一层,通过 Stream ID + Offset 字段信息实现数据的有序性,通过比较两个数据包的 Stream ID 与 Stream Offset ,如果都是一致,就说明这两个数据包的内容一致。
  • 丢失的数据包和重传的数据包 Stream ID 与 Offset 都一致,说明这两个数据包的内容一致。
  • 一个 Packet 报文中可以存放多个 QUIC Frame。
    在这里插入图片描述

Stream

Stream 类型的 Frame 格式(Stream 可以认为就是一条 HTTP 请求):
在这里插入图片描述
在这里插入图片描述

没有队头阻塞的 QUIC

QUIC 给每一个 Stream 都分配了一个独立的滑动窗口,这样使得一个连接上的多个 Stream 之间没有依赖关系,都是相互独立,各自控制的滑动窗口。

在这里插入图片描述

QUIC 流量控制

TCP 流量控制是通过让「接收方」告诉「发送方」,它(接收方)的接收窗口有多大,从而让 「发送方」根据「接收方」的实际接收能力控制发送的数据量

在这里插入图片描述

在这里插入图片描述

TCP 连接建立(三次握手过程)

  • seq:序列号,用于标识TCP报文段的顺序。
  • ack:确认编号(Acknowledgement Number),用于确认已接收到的数据字节的序列号。
  • syn:同步序列编号(Synchronize Sequence Numbers),用于建立TCP连接的握手过程中的同步标志。

ISN 随机产生

  • ISN:初始序列号(Inital Sequence Number),连接建立时生成的初始序列号,是客户端随机产生的一个值。

ISN 随机生成算法:

  • ISN = M + F(localhost, localport, remotehost, remoteport)
  • M 是一个计时器,这个计时器每隔 4 微秒加 1
  • F 是一个 Hash 算法,根据源 IP、目的 IP、源端口、目的端口生成一个随机数值

随机数是会基于时钟计时器递增的,基本不可能会随机成一样的初始化序列号。

TCP 连接全过程各种状态

  • LISTEN:侦听来自远方的TCP端口的连接请求
  • SYN-SENT:再发送连接请求后等待匹配的连接请求(客户端)
  • SYN-RECEIVED:再收到和发送一个连接请求后等待对方对连接请求的确认(服务器)
  • ESTABLISHED:代表一个打开的连接
  • FIN-WAIT-1:等待远程TCP连接中断请求,或先前的连接中断请求的确认
  • FIN-WAIT-2:从远程TCP等待连接中断请求
  • CLOSE-WAIT:等待从本地用户发来的连接中断请求
  • CLOSING:等待远程TCP对连接中断的确认
  • LAST-ACK:等待原来的发向远程TCP的连接中断请求的确认
  • TIME-WAIT:等待足够的时间以确保远程TCP接收到连接中断请求的确认
  • CLOSED:没有任何连接状态

在这里插入图片描述

  • 一开始,客户端和服务端都处于 CLOSE 状态。先是服务端主动监听某个端口,处于 LISTEN 状态。

在这里插入图片描述

  • 客户端将第一个 SYN 报文发送给服务端,表示向服务端发起连接,该报文不包含应用层数据,之后客户端处于 SYN-SENT 状态。

在这里插入图片描述

  • 服务端将第二个报文发给客户端,该报文也不包含应用层数据,之后服务端处于 SYN-RCVD 状态。

在这里插入图片描述

  • 第三个报文发送给服务端,这次报文可以携带客户到服务端的数据,之后客户端处于 ESTABLISHED 状态。
  • 第三次握手是可以携带数据的,前两次握手是不可以携带数据的

一旦完成三次握手,双方都处于 ESTABLISHED 状态,此时连接就已建立完成,客户端和服务端就可以相互发送数据了。

为什么是“三次”

1. 避免历史连接(主要原因)

  • 三次握手才可以阻止重复历史连接的初始化(主要原因)。

在这里插入图片描述

  • 「旧 SYN 报文」称为历史连接
  • TCP 使用三次握手建立连接的最主要原因就是防止「历史连接」初始化了连接。

2. 同步双方初始序列号

三次握手才可以同步双方的初始序列号。

TCP 协议的通信双方, 都必须维护一个「序列号」,序列号的作用:
在这里插入图片描述

在这里插入图片描述

3. 避免资源浪费

三次握手才可以避免资源浪费。
在这里插入图片描述

两次握手会造成消息滞留情况下,服务端重复接受无用的连接请求 SYN 报文,而造成重复分配资源。\

总结

TCP 建立连接时,通过三次握手能防止历史连接的建立,能减少双方不必要的资源开销,能帮助双方同步初始化序列号。序列号能够保证数据包不重复、不丢弃和按序传输。

不使用「两次握手」和「四次握手」的原因:

  • 「两次握手」:无法防止历史连接的建立,会造成双方资源的浪费,也无法可靠的同步双方序列号;
  • 「四次握手」:三次握手就已经理论上最少可靠连接建立,所以不需要使用更多的通信次数。

第一次握手丢失

在这里插入图片描述
在这里插入图片描述

  • tcp_syn_retries:内核参数,控制客户端的 SYN 报文最大重传次数。可以自定义的,默认值一般是 5。
  • 如果客户端迟迟收不到服务端的 SYN-ACK 报文(第二次握手),就会触发 「超时重传」机制,重传 SYN 报文,而且重传的 SYN 报文的序列号都是一样的 。每次超时的时间是上一次的 2 倍

第二次握手丢失

在这里插入图片描述

在这里插入图片描述

  • tcp_synack_retries:内核参数,决定SYN-ACK 报文的最大重传次数。默认值是 5。

  • 当第二次握手丢失了,客户端和服务端都会重传:

    在这里插入图片描述

第三次握手丢失

  • ACK 报文是不会有重传的,当 ACK 丢失了,就由对方重传对应的报文。

在这里插入图片描述

在这里插入图片描述

TCP Fast Open(绕过三次握手发送数据)

在 Linux 3.7 内核版本之后,提供了 TCP Fast Open 功能,这个功能可以减少 TCP 连接建立的时延。

工作方式

在这里插入图片描述

  • 第一次发起 HTTP GET 请求的时候,还是需要正常的三次握手流程。
  • 之后发起 HTTP GET 请求的时候,可以绕过三次握手,这就减少了握手带来的 1 个 RTT 的时间消耗。

tcp_fastopn

在 Linux 系统中,可以通过设置 tcp_fastopn 内核参数,来打开 Fast Open 功能:

  • tcp_fastopn 各个值的意义:
    • 0 关闭
    • 1 作为客户端使用 Fast Open 功能
    • 2 作为服务端使用 Fast Open 功能
    • 3 无论作为客户端还是服务器,都可以使用 Fast Open 功能

TCP Fast Open 功能需要客户端和服务端同时支持,才有效果。

TCP 半连接和全连接队列

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

  • 不管是半连接队列还是全连接队列,都有最大长度限制,超过限制时,默认情况都会丢弃报文,或返回 RST 包。
  • 当服务端并发处理大量请求时,如果 TCP 全连接队列过小,就容易溢出。发生TCP 全连接队溢出的时候,后续的请求就会被丢弃,这样就会出现服务端请求数量上不去的现象。

在这里插入图片描述

因队列长度的关系而被丢弃的条件

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

  • 半连接队列最大值不是单单由 max_syn_backlog 决定,还跟 somaxconn 和 backlog 有关系。
    在这里插入图片描述

SYN 攻击

方式

  • SYN 攻击方式最直接的表现就会把 TCP 半连接队列打满。
    在这里插入图片描述

  • 这样当 TCP 半连接队列满了,后续再在收到 SYN 报文就会丢弃,导致客户端无法和服务端建立连接。

避免方法

在这里插入图片描述

syncookies

开启 syncookies 功能就可以在不使用 SYN 半连接队列的情况下成功建立连接。
在这里插入图片描述

在这里插入图片描述

那么在应对 SYN 攻击时,只需要设置为 1 。

优化 TCP 三次握手

在这里插入图片描述

TCP 连接断开(四次挥手过程)

过程

在这里插入图片描述

在这里插入图片描述

相关函数(close & shutdown)

  • close 函数同时 socket 关闭发送方向和读取方向,也就是 socket 不再有发送和接收数据的能力。

    • 调用了 close 函数的一方的连接叫做**「孤儿连接」**, 用 netstat -p 命令,会发现连接对应的进程名为空。
  • shutdown 函数,可以指定只关闭一个方向的连接

    • SHUT_RD(0):关闭连接的**「读」**这个方向
    • SHUT_WR(1):关闭连接的 「写」 这个方向, 这就是常被称为 「半关闭」 的连接。
    • SHUT_RDWR(2):相当于 SHUT_RD 和 SHUT_WR 操作各一次,关闭套接字的读和写两个方向在这里插入图片描述
      在这里插入图片描述

为什么是“四次”

服务器收到客户端的 FIN 报文时,内核会马上回一个 ACK 应答报文,但是服务端应用程序可能还有数据要发送,所以并不能马上发送 FIN 报文,而是将发送 FIN 报文的控制权交给服务端应用程序

在这里插入图片描述

从上面过程可知,服务端通常需要等待完成数据的发送和处理,所以服务端的ACK 和 FIN 一般都会分开发送,因此是需要四次挥手。

  • FIN 报文不一定得调用关闭连接的函数才会发送。
    • 如果进程退出了,不管是不是正常退出,还是异常退出(如进程崩溃),内核都会发送 FIN 报文,与对方完成四次挥手。

四次挥手变成三次挥手

当被动关闭方(上图的服务端)在 TCP 挥手过程中,「没有数据要发送」并且「开启了 TCP 延迟确认机制」,那么第二和第三次挥手就会合并传输,这样就出现了三次挥手。

在这里插入图片描述
TCP 延迟确认机制是默认开启的,所以导致抓包时,看见三次挥手的次数比四次挥手还多。

TCP 延迟确认机制

为了解决 ACK 传输效率低问题,衍生出了 TCP 延迟确认

TCP 延迟确认的策略:

  • 当有响应数据要发送时,ACK 会随着响应数据一起立刻发送给对方
  • 当没有响应数据要发送时,ACK 将会延迟一段时间,以等待是否有响应数据可以一起发送
  • 如果在延迟等待发送 ACK 期间,对方的第二个数据报文又到达了,这时就会立刻发送 ACK
    在这里插入图片描述
  • 出现三次挥手现象,是因为 TCP 延迟确认机制导致的

第一次挥手丢失

在这里插入图片描述

在这里插入图片描述

第二次挥手丢失

在这里插入图片描述
在这里插入图片描述

第三次挥手丢失

在这里插入图片描述

在这里插入图片描述

第四次挥手丢失

在这里插入图片描述

在这里插入图片描述

优化四次挥手

  • 客户端和服务端双方都可以主动断开连接,通常先关闭连接的一方称为主动方,后关闭连接的一方称为被动方
  • 主动关闭连接的,才有 TIME_WAIT 状态。

在这里插入图片描述

TCP 优化数据传输

在这里插入图片描述


网站公告

今日签到

点亮在社区的每一天
去签到