传输层协议TCP(二)

复制代码
接上文:

文章目录


前言

(6)流量控制

  • 如果客户端发送很多数据,导致服务端的缓冲区被打满,这个时候如果服务端继续发送, 就会造成丢包, 继而引起丢包重传等等⼀系列连锁反应。这时应减少客户端的发送量,调整至服务端能接收的速度。

  • 当服务端接收缓冲区剩余空间很大时,客户端也会增加发送量,最大限度的提高效率。

  • 所以流量控制不只是减少网络吞吐量,也会增加网络吞吐量。

  • TCP支持根据接收端的处理能力, 来决定发送端的发送速度 . 这个机制就叫做流量控制

  • 接收方的接收能力是由其接收缓冲区的剩余空间大小决定的。即服务端的接收能力取决于他的接收缓冲区剩余空间大小。

  • 报头中16位窗口大小 表示的是其接收缓冲区剩余空间的大小,并通过确认应答返回给发送端。

  • 窗⼝大小字段越大, 说明网络的吞吐量越高;

  • 接收端⼀旦发现自己的缓冲区快满了, 就会将窗口大小设置成⼀个更小的值通知给发送端。发送端接受到这个窗口之后, 就会减慢自己的发送速度;

  • 如果接收端缓冲区满了, 就会将窗口置为0; 这时发送方不再发送数据, 而是会定期发送⼀个窗口探测 数据段, 使接收端把窗口大小告诉发送端.

  • 16位窗口大小中: 16位数字最大表示65535, 那么TCP窗口最大就是65535字节吗?实际上, TCP首部40字节选项中还包含了⼀个窗口扩⼤因子M, 实际窗口大小是 16位窗口大小的值左移 M 位;

(7)连接管理机制

TCP建立连接时要进行三次握手, 断开连接时要进行四次挥手

在实际通信中,一个服务端会同多个客户端进行通信,所以TCP报文就有各种各样的类型,这就要求服务端能够区分不同的TCP报文类型。而报文中标志位的存在就是为了区分报文类型

  • ACK

    该标志位置1表明自己是一个确认报文,指示确认序号字段有效,确认已收到数据。因为有捎带应答机制,所以大部分TCP报文,ACK都是1。

  • SYN

    同步标志位,表明建立连接的请求,是三次握手的核心标志。

  • FIN

    连接断开标志位,通信结束时,进行挥手协商。

服务端状态转化:

  • [CLOSED -> LISTEN]

    服务器端调用listen后进入LISTEN状态,等待客户端连接;

  • [LISTEN -> SYN_RCVD]

    当收到SYN时,即进入SYN_RCVD状态。一旦监听到连接请求(同步报文段),就将该连接放入内核等待队列中,并向客户端发送SYN确认报文.

  • [SYN_RCVD -> ESTABLISHED]

    服务器端一旦收到客户端的确认报文,就进入ESTABLISHED状态,可以进行读写数据了.

  • [ESTABLISHED -> CLOSE_WAIT]

    当客户端主动关闭连接(调用close),服务器会收到结束报文段,服务器返回确认报文段并进入CLOSE_WAIT;

  • [CLOSE_WAIT -> LAST_ACK]

    进入CLOSE_WAIT后说明服务器准备关闭连接(需要处理完之前的数据);当服务器真正调用close关闭连接时,会向客户端发送FIN,此时服务器进入LAST_ACK状态,等待最后一个ACK到来(这个ACK是客户端确认收到了FIN)

  • [LAST_ACK -> CLOSED]

    服务器收到了对FIN的ACK,彻底关闭连接.

客户端状态变化:

  • [CLOSED -> SYN_SENT]

    客户端调用connect,发送同步报文段。

  • [SYN_SENT -> ESTABLISHED]

    当发送ACK时,即进入ESTABLISHED状态,开始读写数据。

  • [ESTABLISHED -> FIN_WAIT_1]

    客户端主动调用close时,向服务器发送结束报文段,同时进入FIN_WAIT_1。

  • [FIN_WAIT_1 -> FIN_WAIT_2]

    客户端收到服务器对结束报文段的确认,则进入FIN_WAIT_2,开始等待服务器的结束报文段。

  • [FIN_WAIT_2 -> TIME_WAIT]

    客户端收到服务器发来的结束报文段,进入TIME_WAIT,并发出LAST_ACK。

  • [TIME_WAIT -> CLOSED]

    客户端要等待一个2MSL(Max Segment Life,报文最大生存时间)的时间,才会进入CLOSED状态。等待历史游离的报文在网络中消散。

全双工TCP连接 = 两个独立的单向通道
客户端 → 服务器通道: 用于客户端发送数据 (client发送缓冲区→ server接收缓冲区)
客户端 ← 服务器通道: 用于服务器发送数据 (client接收缓冲区← server发送缓冲区)

注意:
三次握手是双方操作系统自动完成的

  1. accept并不参与三次握手,accept只是等待三次握手完成。
    connect触发三次握手,等连接成功后再返回
  2. 三次握手的最后一个ACK是否被对方收到我并不确定
  3. 客户端往往先认为建立好连接,最后一个ACK还需要再网络中跑一会儿服务端才能收到
  4. 为什么要进行三次握手?三次握手的本质是四次握手,因为做了捎带应答,所以变成了三次握手,并且三次握手可以以最小次数验证通信的全双工信道时通畅的。
  5. 三次握手完成后,一条连接就被建立起来,一条连接就和一个文件(fd)对应。服务端操作系统肯定维护多条连接(多个客户端访问),客户端操作系统也要维护多条连接(要访问不同的服务端,微信、QQ等)

四次挥手要征得双方同意,因为TCP是全双工的,要关闭两个朝向上的连接

  1. close是用来关闭两个方向上的连接,即发送和接收都会被关闭。还有一个系统调用shutdown,可以只关闭读,只关闭写。调用 shutdown()(尤其是 shutdown(SHUT_WR) 或 shutdown(SHUT_RDWR))通常会使"主动关闭的一方"进入 FIN_WAIT_1 状态,随后可能进入 FIN_WAIT_2。 是否进入 FIN_WAIT,关键在于:有没有发送 FIN 。当调用shutdown(SHUT_RD),内核不会发送FIN。
    举例说明shutdown的使用场景:

    客户端向服务器发送一个请求,然后调用shutdown(SHUT_WR)关闭写方向,表示客户端已经发送完请求,然后等待服务器响应。服务器在收到FIN后,知道客户端已经发送完请求,但还可以继续向客户端发送响应。客户端在收到服务器的响应后,再调用close完全关闭连接。

  2. close和shutdown都会影响TCP连接的状态,但close会释放套接字描述符,而shutdown不会。因此,在调用shutdown后,仍然需要调用close来释放描述符。

  3. 客户端调用close(),那么它会发送FIN,然后进入FIN_WAIT_1,接着收到ACK后进入FIN_WAIT_2。在FIN_WAIT_2状态,内核仍然会接收数据并保存在接收缓冲区,这些数据会被内核接收并确认,然后丢弃。但是应用程序已经不能通过已经close的文件描述符来读取数据了,因为close之后文件描述符已经无效。

  4. 为什么四次挥手不进行捎带应答变成三次挥手呢?因为服务器收到客户端的FIN后,可能还有数据要发送,所以先确认,等数据发送完再发送自己的FIN。因此,ACK和FIN分开发送,导致需要四次 。如果服务器收到FIN后没有数据要发送,那么可以将ACK和FIN合并发送,这样就变成三次挥手了。TCP协议允许这种情况,不过一般情况下,ACK和FIN是分开发的,因为应用程序可能还需要处理数据。

  5. 因此,四次挥手是为了保证双方都完成数据发送,并且确保对方收到关闭请求的确认。

为什么要设计TIME_WAIT状态?主要有两个原因:

  1. 可靠地实现TCP全双工连接的终止。

    如果主动关闭方发送的最后一个ACK丢失,被动关闭方会重传FIN。这样,主动关闭方在TIME_WAIT状态下可以再次发送ACK。

    如果没有TIME_WAIT状态,那么主动关闭方在发送ACK后立即关闭连接,若该ACK丢失,被动关闭方将永远收到不到ACK,导致连接无法正常关闭。

  2. 允许旧的重复数据包在网络中消逝,避免被新的连接误接收。

    考虑一个连接关闭后,又立即在相同的IP地址和端口之间建立新的连接。旧连接的数据包可能由于网络延迟而在新连接建立后才到达。

    如果没有TIME_WAIT状态,这些旧数据包可能会被新连接接收,造成数据混乱。而TIME_WAIT状态确保在连接关闭后,至少等待2MSL的时间,

    使得旧连接的所有数据包都在网络中消失,这样新连接就不会收到旧数据。

为什么要设计 CLOSE_WAIT 状态?主要有两个原因:

  1. 保证被动关闭方能够完成剩余数据的发送,正确支持 TCP 半关闭语义。

    当一端收到对方发送的 FIN 时,表示对方已经关闭了发送方向,但并不意味着本端也已经完成了数据发送。本端可能仍然有尚未发送完的业务数据需要发送给对方。如果在收到 FIN 后立即关闭连接并发送 FIN,将会导致这些尚未发送的数据被中断 ,从而破坏 TCP 全双工通信中"半关闭"的语义。CLOSE_WAIT 状态的存在,使被动关闭方在确认对端关闭之后,仍然可以继续向对端发送数据,直到应用层明确调用 close() 或 shutdown(SHUT_WR),再由内核发送 FIN 完成本端的关闭。

  2. 将连接最终关闭的控制权交给应用层,避免协议层替应用层做决定。
    TCP 协议栈只能感知对端是否发送了 FIN,却无法判断本地应用层的业务逻辑是否已经处理完成。如果在收到 FIN 后由内核自动完成连接关闭,那么应用层将失去对连接生命周期的控制权,可能导致业务处理尚未结束就被强制终止。CLOSE_WAIT 状态的设计,使协议层在收到 FIN 后仅负责确认对端的关闭,而将"何时真正关闭连接"的决定权交由应用层,从而实现协议层与应用层的解耦,保证连接关闭过程符合实际业务需求。

相关推荐
HIT_Weston21 小时前
85、【Ubuntu】【Hugo】搭建私人博客:文章目录(四)
linux·ubuntu·html5
optimistic_chen21 小时前
【Redis 系列】常用数据结构---SET类型
linux·数据结构·数据库·redis·set·数据类型·命令行
源远流长jerry1 天前
http协议和https协议的连接流程
网络·http·https
独自破碎E1 天前
整理一些Linux的常用命令
linux·运维·服务器
懒猫爱上鱼1 天前
Android Service启动流程详解
面试
松涛和鸣1 天前
44、HTML与HTTP服务器交互笔记
linux·运维·服务器·http·链表·html
Konwledging1 天前
等待队列wait_queue
linux
居然是阿宋1 天前
DHCP深度解析:从自动连接到七层之谜
网络