个人主页:chian-ocean

文章专栏-NET

前言:

在现代互联网中,传输控制协议(TCP)是确保数据可靠传输的核心协议之一。作为一种面向连接的协议,TCP在互联网中的重要性不言而喻。从网页加载到文件传输,几乎所有的互联网通信都依赖于TCP协议来确保数据完整、准确地到达目的地。

image-20250615163112131

TCP的特点

  • 为了通过 IP 数据报实现可靠传输,需要考虑很多事情,例如数据的破坏、丢包、重复以及分片顺序混乱等问题。如不能解决这些问题,也就无法谈起可靠传输。
  • TCP 通过检验和、序列号、确认应答、重发控制、连接管理以及窗口控制等机制实现可靠传输。

通过序列号与确认应答提高可靠

正常应答

  • 在 TCP 中,当发送的数据到达接收方时,接收方会通过确认应答(ACK)通知发送方数据已接收。
  • 就像对话中,通过点头或提问确认对方是否理解内容一样,确认应答(ACK)表示对方已收到数据。如果对方没有反应或未听清,会通过提问表示需要确认,这类似于否定确认应答(NACK)。

image-20250613225140498

超时重传

数据在传输时候丢包

  • 在一定时间内没有等到确认应答,发送端就可以认为数据已经丢失,并进行重发。因此,即使产生了丢包,仍然能够保证数据能够到达对端,实现可靠传输。如图:

image-20250613225545621

接收端未回应

  • 未收到确认应答并不意味着数据一定丢失。也有可能是数据对方已经收到,只是返回的确认应答在途中丢失。这种情况也会导致发送端没有收到确认应答,而认为数据没有到达目的地,从而进行重新发送。如图 :

image-20250613225710304

简单来说,TCP 通过 序列号确认应答(ACK) 实现可靠的数据传输。由于网络中的延迟或丢包,接收方可能会收到重复的数据包。为了避免这些重复的数据被处理,接收方需要能够识别并丢弃它们。这个过程通过 序列号 来完成,每个数据包都会被标记一个编号,接收方检查数据包的序列号,并根据它决定是否接收数据。如果接收方收到的序列号符合预期,它就会发送一个确认应答(ACK)给发送方,表示数据已经接收。这样,发送方就可以确定哪些数据已经成功传输,确保数据传输的可靠性。

image-20250613230431630

  1. 数据分段:数据从源主机发送到目标主机,按序列号分段,每个数据段有一个唯一的序列号(如从 1 到 1000、1001 到 2000 等)。
  2. 确认应答(ACK):接收方收到数据后,会发送确认应答给发送方,告知下一个期待接收的数据字节。例如,接收方收到 1 到 1000 的数据后,会发送确认号 1001,表示接收已完成,准备接收接下来的数据。
  3. 时序与流控制:每个数据段会根据顺序发送,接收方根据序列号确认已接收的数据,确保数据传输的可靠性。每个数据包的大小由 MSS(最大报文段大小)限制,在图中为 1000 字节。

三次握手和四次挥手

三次握手

  • TCP 提供面向连接的通信传输面向连接是指在数据通信开始之前,先做好通信两端之间的准备工作。

  • UDP 是一种 面向无连接的通信协议,因此不检查对端是否可以通信,直接将 UDP 包发送出去。TCP 与此相反,它在数据通信之前,通过 TCP 首部发送一个 SYN 包 作为建立连接的请求,等待确认应答。如果对端发送确认应答,则认为可以进行数据通信。如果对端的确认应答未能到达,就不能进行数据通信。此外,在通信结束时会进行 断开连接的处理(FIN 包)

  • **可以使用 TCP 首部用于控制的字段来管理 TCP 连接。一个连接的建立与断开,正常达到的要求是必须先 **发送 7 个包 才能完成。

image-20250616224517575

几点要点:

  • 客户端在服务器在发送SYNACK确定建立连接成功的,但是服务器在客户回复最后一个ACK确定建立成功的。
  • 三次握手服务器发送SYN-ACK,本质上是使用了捎带应答,当然也可以理解为4次握手
  • 如果在第一次SYN的时候就建立连接呢?(客户端再发起第一次SYN的时候建立连接?还是在回复ACK的时候建立连接呢?)下面讨论

一次握手会什么样

  1. 客户端再发起第一次SYN的时候建立连接
  2. 客户端发送 SYN 包:客户端向服务器发送连接请求(SYN 包)。
  3. 服务器收到 SYN 包并开始发送数据:服务器收到请求后,假设连接已建立,直接开始向客户端发送数据。

影响:

  • 此时服务端直直接建立连接,但是,客户端如果突然网络延迟,或者网络出现问题?服务端还会维持连接的请求。
  • 或者此时如果无数个请求过来,会引发SYN洪水:
SYN 洪水(SYN Flood)
  • 是一种常见的 拒绝服务攻击(DoS 攻击),利用了 TCP 连接建立过程中的漏洞,特别是在 三次握手 的过程中。

SYN 洪水攻击 中,攻击者发送大量的 SYN 包到目标服务器,但不完成第三次握手(即不发送 ACK 包)。这会导致服务器资源被占用,无法处理正常的连接请求,从而导致 拒绝服务。攻击者利用这一点使得服务器 无法正常响应合法用户的请求

SYN 洪水攻击的影响:

  1. 资源耗尽:服务器分配资源但无法释放,导致系统无法处理正常请求。
  2. 拒绝服务(DoS):攻击填满连接队列,使合法用户无法建立连接,造成服务不可用。
  3. 连接队列溢出:服务器的连接队列被攻击者的 SYN 包填满,无法处理新的连接请求。
  4. 系统性能下降:攻击消耗计算资源,导致服务器性能下降,可能发生崩溃。
  5. 增加网络带宽压力:大量 SYN 包消耗带宽,影响正常网络流量的传输。

两次握手什么样子

  1. 客户端在ACK回复的时候建立

  2. 客户端发送 SYN 包:客户端向服务器发送一个 SYN 包,表示请求建立连接。

  3. 服务器响应 SYN-ACK 包:服务器收到 SYN 包后,回应一个 SYN-ACK 包,表示同意建立连接。

影响:

  • 客户端如果突然网络延迟,或者网络出现问题,会影响服务器的资源。不能将服务器进行充分的利用。

四次挥手:

image-20250616172914027

  1. 想象两个人在咖啡店里喝咖啡,聊天完毕后,他们决定一起离开。为了避免任何一个人误解对方的意图,他们通过四个步骤来确认,确保双方都准备好离开。

    1. 客户端发起告别:
    • 你(客户端)决定离开,于是你告诉朋友:“我准备离开了。”
    • 这就像客户端发送 FIN 包,表示它没有更多的数据要发送了,并且希望开始关闭连接。
    2. 朋友确认知道了:
    • 你的朋友(服务器)听到后回应:“好的,我知道了,你先等一下,我也准备好离开了。”
    • 这就像服务器收到 FIN 包后,发送一个 ACK 包确认接收,并表示它准备好关闭连接。
    3. 朋友准备离开:
    • 你的朋友(服务器)决定离开,于是他说:“我准备走了,你也准备好了吗?”
    • 这就像服务器发出 FIN 包,表示它也没有更多的数据要发送,准备关闭连接。
    4. 你确认朋友也准备好:
    • 你(客户端)听到朋友的话,然后确认:“好的,你准备好了,我们一起走!”
    • 这就像客户端收到服务器的 FIN 包后,发送一个 ACK 包确认,表示双方都准备好关闭连接。

深入理解三次握手

image-20250615163606913

全连接队列和半连接队伍

定义:

  • 全连接队列 是服务端操作系统内核维护的一个队列,存放已经完成三次握手且尚未被应用层调用 accept 接受的连接。这些连接可以被认为是已经建立的 TCP 连接,但它们还没有交给应用程序(通过 accept)来进行数据交互。
  • 半连接队列:当客户端发送SYN请求时,服务器将连接放入半连接队列。此时,连接还处于三次握手的过程中,等待客户端的确认(ACK)。服务器向客户端发送SYN-ACK包后,连接进入SYN-RECV状态,仍在半连接队列中等待客户端的最终确认。或者是直到客户端的 ACK 包未到达服务端。

作用

  • 它的作用是缓冲那些已经完成三次握手的连接请求。服务端通过 accept 接受连接,之后将连接从全连接队列中移除,并将其交给应用程序的套接字进行进一步的数据交换。

大小:

  • 全连接队列的大小是有限的,通常由操作系统配置。在 Linux 系统中,可以通过 /proc/sys/net/core/somaxconn 来查看或调整全连接队列的最大大小。
cat /proc/sys/net/core/somaxconn

image-20250616160537614

  • 每个服务端的监听套接字在调用 listen() 时,也会指定一个队列的长度,这个参数的值通常表示的是:在连接完成三次握手但尚未被 accept 接收时,最多能保持多少个连接等待被应用层处理。
listen(socket_fd, backlog);

其中,backlog 参数即为全连接队列的长度。

  • 如果队列满了,新到来的连接请求将被拒绝或丢弃。
  • 如果有空闲位置,新的连接请求会加入队列。

如下图:

  • 上面服务端,是服务器版本Centos,公网IP为119.3.219.187。左上启动服务器,右上监控服务器网络状态netstat.
  • 下面客户端,是服务器版本Ubuntu,公网IP为58.87.92.219。左下启动了三台客户端同时访问服务器,右下监控客户端网络状态netstat.
  • listen()监听backlog = 1,此时全连接队伍长度大小是backlog + 1 = 2
  • 右上可以观察到进程PID4839448400服务器网络状态为ESTABLISHED,而进程48406,服务器网络状态处于SYN_RECV。这时的PID是4839448400处于全连接队列中,PID是48406处于半连接队列中。全连接队伍已经满了,服务器只能维护它一段时间,放在半连接队伍。
  • 右下监控客户端均进程均为ESTABLISHED,客户端接受SYN-ACK请求,将自己状态转换为ESTABLISHED,同时向服务端发送SYN但是服务端未确认。此时仍然处于半连接状态

image-20250616162236036

**为什么维护半连接队伍:**准确是为了提高效率。下面有一个例子:

  • 半连接队列就像餐厅的候客区,存放那些已经表示有意愿吃饭(发出了SYN)的客人,但还没有确认是否真的要入座(客户端还没有回复ACK确认)。

  • 只有当你确认了(客户端发送了ACK),你才会被正式安排到餐桌上(进入全连接队列),可以开始吃饭(正式建立连接进行数据交换)

深入理解四次挥手

image-20250616175518318

  • 上面服务端,是服务器版本Centos,公网IP为119.3.219.187。左上启动服务器,右上监控服务器网络状态netstat.

  • 下面客户端,是服务器版本Ubuntu,公网IP为58.87.92.219。左下启动了三台客户端同时访问服务器,右下监控客户端网络状态netstat.

  • 客户端向服务发送FIN,服务器接受FIN,将自己的状态设置为CLOSE_WAIT 并发送 ACK。客户端接收ACK将自己的状态转换成FIN_WAIT2

  • 客户端准备向客户端发送FIN,此时客户端处于TIME_WAIT中,客户端接受之后也处在TIME_WAIT中。

image-20250616174632499

TIME_WAIT 状态的意义:

  1. 确保确认包到达对方:客户端在关闭连接后,进入 TIME_WAIT 状态,确保它的 ACK 确认包能够到达服务器,避免服务器未收到确认而重发数据包。

  2. 避免旧数据包干扰:等待一段时间,确保任何延迟的旧数据包不会影响后续的新连接,避免数据混乱。

  3. 资源清理:在 TIME_WAIT 状态期间,操作系统可以回收与该连接相关的资源(如端口),确保系统资源得到正确释放。

  4. 这段时间通常是 2倍最大段生命周期(2MSL)(存在最大的生命周期,并不是传送的生命周期,存在最大的生命周期,可能存在在网络中进行拥堵等),确保服务器可以在这个时间内接收到客户端的 ACK 包。如果客户端不进入 TIME_WAIT 状态,而是直接关闭连接,可能会导致服务器未收到确认而重新发送数据包。

  5. 防止端口重用问题:确保端口不会过早回收,避免新的连接使用相同端口时混淆旧连接的数,这就是为什么断开连接二次BIND不成功。如图:

image-20250616180724680

解决地址端口复用

只需要在bind之前加上这句话,设置一下参数就好了。

int opt = 1;
setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &opt, sizeof(opt));
参数说明
  1. sockfd:套接字文件描述符,是通过 socket() 函数创建的套接字标识符,用于指定哪个套接字的选项需要设置。
  2. level:选项所在的协议层。常见的层级包括:
    • SOL_SOCKET:套接字级别,设置的是套接字的通用选项。
    • IPPROTO_TCP:TCP协议层,设置的是与TCP协议相关的选项。
    • IPPROTO_IP:IP协议层,设置的是与IP协议相关的选项。
  3. optname:要设置的选项名称。例如,SO_REUSEADDR(允许端口重用)、SO_RCVBUF(接收缓冲区大小)、TCP_NODELAY(禁用Nagle算法)等。
  4. optval:指向存储选项值的指针。值的类型和大小由选项本身决定。例如,SO_REUSEADDR 可能是一个 int 类型的值,表示启用(1)或禁用(0)该选项。
  5. optlenoptval 的大小,通常是 sizeof(optval)
常见的选项
  • SO_REUSEADDR:允许快速重用端口,通常用于服务重启时。
  • SO_RCVBUFSO_SNDBUF:设置接收和发送缓冲区的大小。
  • TCP_NODELAY:禁用Nagle算法,减少延迟。
  • SO_KEEPALIVE:启用连接保持活动检测。

image-20250616211908902

利用窗口控制提高速度

TCP 以 1 个段为单位,每发一个段进行一次确认应答的处理,如图,这种传输方式有一个缺点。那就是,包含的往返时间越长,通信性能就越低。

image-20250616213556057

为了解决这个问题,TCP 引入了窗口这个概念。即使在往返时间较长的情况下,它也能控制网络性能的下降。如图所示,确认应答不再是以每个分段,而是以更大的单位进行确认时,传发时间将被大幅度的缩短。也就是说,发送端主机在发送了一个段以后不需要一直等待确认应答,而是继续发送。

image-20250616213715080

简单来说,TCP的滑动窗口机制让发送方在不等待每一段确认的情况下,连续发送多个数据段,提高了传输效率。窗口大小决定了在确认之前,发送方可以一次性发送多少数据。发送端在发送数据后,即使没有收到确认,也可以继续发送下一个数据。当接收方确认了某些数据后,发送方就会“滑动”窗口,开始发送新的数据。

  • 数据滑动发送:在窗口内的数据被发送,不需要等待每一段的确认。
  • 重传机制:如果数据丢失,发送方会重新发送那些没有收到确认的数据。
  • 缓冲区:发送方会在缓冲区保存未确认的数据,直到收到确认应答后再清除这些数据。

image-20250616213929687

利用窗口重新发送

TCP协议是可以允许应答丢失的:

TCP 使用 延迟应答 机制,即接收方发送的一个确认包(ACK)确认了多个数据包。即使某些 ACK 包丢失,发送方也不会丢失数据确认信息,因为其他 ACK 包仍然有效。

例如:

  • 如果接收方已收到数据包 1000、2000 和 3000,它会发送确认包确认收到序列号 40001(即确认了数据包 1000到 3000 的接收)。
  • 如果确认包丢失,发送方会根据 超时重传重复 ACK 来恢复丢失的确认。

image-20250616215037376

快重传

当某一报文段丢失后,发送端会一直收到序列号为 1001 的确认应答,这个确认应答像是在提醒发送端“我想接收的是从 1001 开始的数据”。因此,在窗口比较大,又出现报文段丢失的情况下,同一个序号的确认应答将会被不断返回。而发送端主机如果连续 3 次收到同一个确认应答,就会将其对应的数据进行重发。这种机制比之前提到的超时管理更高效,因此也被称作高速重发控制。

image-20250616220539296

拥塞控制

  • TCP 的窗口控制机制使得发送方可以在不等待每个数据包确认的情况下,持续发送数据。这样可以提高传输效率。不过,如果一开始就发送大量数据,可能会导致网络拥堵,影响其他通信。
  • 为了解决这个问题,TCP 在连接开始时采用慢启动策略,逐步增加发送数据的量,避免一开始就发送太多数据,导致网络拥堵。

image-20250616221025014

为了避免在发送数据时一次性发送过多数据导致网络拥堵,TCP 在连接开始时采用了 慢启动 策略。它会设置一个初始的发送窗口大小(通常为 1 个数据段),每次收到确认应答后,逐步增加窗口大小。随着每次确认的到来,发送窗口会按比例增大,从而逐步增加发送的数据量,避免网络拥堵。

  1. 慢启动:刚开始时,窗口大小设置为 1 个数据段(1 MSS),每次收到确认后增加窗口大小。
  2. 增大窗口:窗口大小会根据接收到的确认应答逐渐增大,直到达到一个稳定的传输速率。
  3. 避免拥堵:通过慢启动机制,防止一次性发送过多数据导致网络拥堵。

image-20250616221629223

  1. TCP 的通信开始时,并没有设置相应的慢启动值。而是在超时重发时,才会设置为当时拥塞窗口一半的大小。
  2. 每次确认后增长:在慢启动阶段,每当发送方收到一个确认(ACK)时,拥塞窗口大小 就会 加倍,即每收到一个确认,窗口增加一个 MSS。这个过程的增长是 指数型的。窗口到达阈值的时候进行线性增长。
  3. 到达拥塞窗扣的最大值,然后瞬间急剧下降,在慢慢增长。

延迟应答和捎带应答

延迟应答是指接收方在收到数据包后,并不会立即发送确认应答(ACK),而是延迟一段时间,直到接收到更多的数据包,或者等待到合适的时机再一起发送确认应答。这样做的目的是减少 确认应答 包的发送次数,降低网络负载和延

image-20250616224240065

捎带应答(Piggybacking) 是 TCP 协议中的一种优化机制,指的是 在发送数据时,将确认应答(ACK)信息“捎带”在数据包中一起发送,而不是单独发送一个确认包。捎带应答通过减少不必要的确认包的发送,减少了网络负担,提高了数据传输的效率。三次握手就是服务端返回就是一个SYN-AC就是捎带应答。

包后,并不会立即发送确认应答(ACK),而是延迟一段时间,直到接收到更多的数据包,或者等待到合适的时机再一起发送确认应答。这样做的目的是减少 确认应答 包的发送次数,降低网络负载和延

[外链图片转存中…(img-BJOa1kCt-1762229177439)]

捎带应答(Piggybacking) 是 TCP 协议中的一种优化机制,指的是 在发送数据时,将确认应答(ACK)信息“捎带”在数据包中一起发送,而不是单独发送一个确认包。捎带应答通过减少不必要的确认包的发送,减少了网络负担,提高了数据传输的效率。三次握手就是服务端返回就是一个SYN-AC就是捎带应答。

image-20250616224528283

Logo

助力广东及东莞地区开发者,代码托管、在线学习与竞赛、技术交流与分享、资源共享、职业发展,成为松山湖开发者首选的工作与学习平台

更多推荐