做网站开发多少钱,wordpress图片加水印,目前做哪个网站致富,wordpress访问速度文章目录#xff1a; TCP 协议关于可靠性TCP 协议段格式序号与确认序号六个标志位16位窗口大小 确认应答#xff08;ACK#xff09;机制超时重传机制连接管理机制连接建立#xff08;三次握手#xff09;连接终止#xff08;四次挥手#xff09;TIME_WAIT 状态CLOSE_WAI… 文章目录 TCP 协议关于可靠性TCP 协议段格式序号与确认序号六个标志位16位窗口大小 确认应答ACK机制超时重传机制连接管理机制连接建立三次握手连接终止四次挥手TIME_WAIT 状态CLOSE_WAIT 状态 滑动窗口sliding window流量控制Flow control拥塞控制Congestion control延迟应答Delayed Acknowledgment捎带应答面向字节流粘包问题TCP 异常情况基于 TCP 应用层协议TCP 小结 TCP 协议
传输控制协议TCP是 Internet 协议组的主要协议之一。它位于用于提供可靠交互服务的应用程序和网络层之间。它是一种面向连接的通信协议有助于在网络上不同设备之间交换消息。TCP 运行在通过 IP 网络通信的主机上的应用程序之间提供可靠的、有序的、错误检查的字节流。主要的互联网应用如万维网、电子邮件、远程管理和文件传输都依赖于 TCP它是 TCP/IP 协议传输层的一部分。 TCP 协议在如今广泛使用其广泛使用是基于其提供的可靠性保证和强大功能的优势。基于 TCP 上层应用非常丰富多样如HTTP、HTTPS、FTP、SSH 等甚至一些底层的数据库系统如MySQL 也是基于 TCP 进行通信TCP 的可靠性保证对于数据库操作的一致性和可靠性非常重要。
关于可靠性
如今的大部分计算机都是基于冯诺依曼体系结构的。冯诺依曼体系结构的计算机中各个硬件设备输入设备、输出设备、内存、CPU之间通过总线进行通信。在短距离内数据传输的可靠性通常较高。然而当设备之间的距离较远时比如在网络之间进行数据传输传输路线非常长导致数据在传输的过程中出现错误的概率增加。
为了解决长距离通信中的可靠性问题TCP 协议应运而生。通过 TCP 协议数据可以安全、可靠地在网路中传输从而满足现代计算机和网路应用中对数据可靠性的需求。
TCP 协议段格式
TCP 协议由报头和数据组成。报头包含10个必选字段和一个可扩展字段选项。数据部分在报头之后是应用程序协议的有效负载数据。
TCP 协议段的格式如下 TCP 报头中的各个字段的含义如下 源端口16位 它定义了发送数据的应用程序的端口。即源端口地址。 目的端口16位它定了应用程序在接收端的端口。即目的端口地址。 序号32位该字段包含特定会话中数据字节的序列号。 确认序号32位当设置了 ACK 标志时它包含数据字节的下一个序列号并作为对接收到的前一个数据的确认。例如如果接收方收到段号 ‘x’那么它响应 ‘x1’ 作为确认序号。 TCP 报头长度4位它指定报头的长度由报头中的4字节字表示。报头的大小在20到60之间。因此该字段的值介于5到15之间。 6个标志位 1️⃣ URGUrgent表示紧急指针。如果设置了则紧急处理数据。 2️⃣ ACKAcknowledgment表示确认字段有效。客户端发送的初始 SYN 包之后的所有包都应该设置此标志。如果 ACK 设置了0则表示该数据包不包含 ACK。 3️⃣ PSHPush如果设置了该字段则请求接收设备将数据推送到接收应用程序。 4️⃣ RSTReset对方要求重新建立连接我们把携带 RST 标识的报头称为复位报文段。 5️⃣ SYNSynchronize请求建立连接我们把携带 SYN 标识的报文称为同步报文段。 6️⃣ FINFinish用于释放连接不再进行进一步的数据交换我们称携带 FIN 标识的报文为结束报文段。 窗口大小16位包含接收方可以接收的数据大小。该数据用于发送方和接收方之间的流量控制也决定接收方为一个段分配的缓冲区数量。该字段的值由接收方决定。 校验和16位发送端填充CRC检验。接收端校验不通过则认为数据有问题。此外的检验和不光包含 TCP 首部也包含 TCP 数据部分。 紧急指针16位它是一个指针如果 URG 标志设置为1则指向紧急数据字节。它定义了一个值该值将被添加到序列号中以获得最后一个紧急字节的序列号。 ·40字节头部选项它提供了额外的选项。可选字段用32位表示。如果该字段包含的数据小于32位则需要填充来获得剩余的位。
TCP 报头是以结构体的形式存储的。在 TCP 协议的实现中TCP 报头被定义为一个名为 struct tcphdr 的结构体。这个结构体包含了 TCP 的上述字段。每个 TCP 报文都以 struct tcdhdr 结构体的形式存储以便在网络通信过程中进行解析和处理。在操作系统内核中TCP 报头的相关信息会被存储在相应的数据结构中例如 struct sk_buff。 TCP 是如何将报头与有效载荷进行分离的 当 TCP 从底层收到一个报文之后是不知道报文的具体长度的。但是 TCP 报文中的基本报头是20个字节该报头其中的4位标识了该报文的报头长度。
因此当 TCP 收到一个报头之后按照以下步骤进行分离
TCP 首先读取报文的前20个字节这是 TCP 的基本报头部分。在基本报头部分的前4个字节中包含了4位的首部长度字段。通过读取这4位字段TCP 即可获得 TCP 报头的大小。如果首部长度字段的值大于20字节则表示报头中包含了选项字段。TCP 继续从报文中读取额外的字节首部长度字段的值-20这部分字段即为 TCP 报头的选项字段。读取完基本报头和选项字段之后剩余的就是有效载荷部分即数据。
序号与确认序号 在 TCP 通信中什么样才是真正的可靠传输 在进行网络通信时数据的可靠传输是一个复杂的过程。当一方发送数据之后并不能直接保证对端已经成功接收到数据因为在传输过程中可能会发生各种错误。只有当对端主机对发送的消息进行响应时才能保证主机发送的报文对端接收到了。
TCP 要确保双方通信的可靠性主机A向主机B发送一个信息当主机B给主机A回应之后主机A就可以确保上一次发送的数据被主机B收到了。但主机B也需要保证自己发送的数据被主机A接收到了。因此在主机A收到报文之后应该再回应一个响应报文此时又不能保证主机B一定收到若主机B继续应答就形成了一个循环。
只有当一端收到对方的响应数据时才能保证自己上一次发送的数据被对端收到了。但双方通信时总会有一条最新的消息因此不能保证100%可靠。
然而在实际通信中并不需要对所有消息都进行严格的可靠性保证。确保通信的可靠性并不意味着每个消息都必须得到响应而是确保核心数据的可靠传输。核心数据通常是指应用层需要交换的重要信息。例如文件传输中的文件内容、网络会话中的请求和响应内容等。只要这些核心数据能够可靠地传输并得到确认就可以认为通信是可靠的。
对于一些次要的数据如响应数据确实没有必要保证其可靠性。如果对端没有收到这些响应数据可以推断上一次发送的核心数据丢失并进行重传。这就是 TCP 中的确认应答机制。
在互联网中目前无法实现100%可靠性传输但通过确保核心数据的可靠传输并采用确认应答机制就可以满足大部分场景下的可靠性通信需求了。 32位序号 在网络通信中如果一方必须等待上一次发送的数据的响应才发送下一个数据那么通信过程将会变得串行化导致效率低下。
为了提高通信效率TCP 允许发送方连续发送多个报文数据而不需要等待每个报文的响应。关键是要确保每个报文都有对应的响应消息以保证这些报文被对方接收。然而由于不同报文在网络传输中可能选择不同的路径导致报文到达对端主机的顺序与发送顺序可能不同。
为了解决这个问题TCP 报头的32位序列号起到了非常重要的作用其中之一就是保证报文的有序性。发送方在发送每个报文时都会为其分配一个唯一的序列号。接收方通过检查报文序号就可以确定报文的顺序并进行重组。如果报文有缺失可以要求发送发重新发送以确保报文的有序性和可靠性。
TCP 在数据传输过程中对每个字节进行编号即为序列号。每个 TCP 报文段都包含一个序列号字段用于标识该报文中的第一个字节的序列号。
示例
如果发送端要发送3000字节数据每次发送1000字节数据那么需要分为三个 TCP 报文段进行发送。这三个报文段的序列号分别为1、1001、2001。发送端会在 TCP 报文段中填写相应的序列号。接收端收到这三个 TCP 报文段后会根据报文段的序列号对它们进行顺序排列。通过比较报文段的序列号和有效值的字节数接收端可以确定下一个报文对应的序号从而恢复原始数据的顺序。 32位确认序号 确认序号Acknowledgment Number是 TCP 报头中的另一个32位字段用于确认接收端期望从发送端接收的下一个字节的序号。接收端会将下一个期望接收的字节的序号放在确认序号中发送回发送端。通过确认序号发送端可以了解哪些数据已经被接收端成功接收。
在上面的示例中当主机B已经成功接收并处理了序号为1-1000的字节数据那么主机B在发送给主机A的响应数据的报头的确认序号字段填写1001。表示主机B已经将序列号在1001之前的字节数据已经收到了。 如果仅有一套序号机制发送方将序号看作32位序号接收方对数据进行响应时将序号看作是32位确认序号这样也是可以完成通信的。 TCP 协议中为什么要使用两套序号机制 对于 TCP 通信中的双方每个方向的数据流都需要有自己的序号和确认序号。这是因为在全双工通信中双方可能同时发送数据并且需要对对方上次发送的数据进行确认。
发送方使用序号来标识自己当前发送数据的序号以及期望接收到对端的确认序号。接收方在接收到数据之后会使用确认序号来告知发送方以及成功接收到的数据字节序号以及下一次期望接收的数据序号。
通过使用两套序号机制TCP 可以实现以下功能
发送方使用序号来标识发送的数据段确保数据的顺序和完整性。接收方使用确认序号来告知发送方已经接收到的数据以及下一次期望接收的数据序号。也可以在32位序号中填写接收方需要发送给对端的数据字节序号。体现了 TCP 全双工的特性。发送方根据接收到的确认序号来确认对方已经成功接收到的数据数据若有丢失则进行重传操作。
总结TCP 使用两套序号机制是为了支持全双工通信和可靠传输。
六个标志位
在 TCP 连接中标志位被用于指示连接的特定状态或提供一些额外的有用信息例如故障排除目的或处理特定连接的控制。常用的标志位如 “SYN”、“ACK”、“FIN” 等每个标志位占用一个比特位用0表示假1表示真。 SYN TCP中的SYN同步标志位在建立两个主机之间的连接时的初始阶段或三次握手过程中使用。只有发送方和接收方的第一个数据包应该设置这个标志位。它用于同步序列号即告诉对方应该接受哪个序列号。
报文中的 SYN 标志位被设置为1表明该报文是一个连接请求的报文。 ACK 用于确认主机成功接收到的数据包。如果确认号字段包含有效的确认号那么该标志位将被设置。
一般情况下除了第一个请求报文之外其它报文基本都会设置 ACK 标志位。这是因为发送方发送的数据本身具有对接收方之前发送的数据的确认能力。因此在进行数据通信时双方可以同时对彼此上一次发送的数据进行确认和响应。 FIN 用于请求连接终止即当发送方没有更多数据时他会请求连接的终止。这是发送方发送的最后一个数据包。它释放了保留的资源并终止连接。 RST 当发送方检测到 TCP 连接有问题或者认为这次通信不应该存在时使用 RST 来终止连接。当接收方发送给特定主机的报文不被该主机预期时接收方可能会发送 RST 报文。
RST 标志位用于在发生异常情况或出现严重错误时终止连接。他表示发送方或接收方希望立即中断连接并且不再进行进一步的通信。RST 报文可以被视为一种强制性的关闭连接的手段。 URG 当发送端发送了一些被标记为紧急数据的内容时接收端的上层协议需要有机制能够识别和提取这些紧急数据进行优先处理。TCP 协议中使用了紧急指针Urgent Pointer和紧急URG标志位来实现这一目的。
当 URG 标志位被设置时它指示了在报文中存在需要优先处理的紧急数据。紧急指针字段指示了紧急数据的偏移量或位置以便接收方能够准确地识别和处理这些数据。 当 URG 标志位被设置为1时表示报文中存在紧急数据且紧急指针字段的值变得有效。紧急指针字段是一个16位的字段它指示了紧急数据在报文中的偏移量。
紧急指针字段的值表示从 TCP 报文头部开始的偏移量。由于紧急指针字段只有一个它只能标识数据段中的一个位置。这意味着紧急数据的长度被限制为一个字节因为紧急指针只能指示一个字节的位置。具体紧急数据的含义和处理方式应该由应用层协议定义。
在 socket 编程中recv 函数和 send 函数都提供了一个名为 flags 的参数用于指定一些特殊的选项。其中针对紧急数据的处理可以使用 MSG_OOB 选项。 PUSH 该标志位在 TCP 中用于请求立即将数据传递到接收主机而不需要等待发送缓冲区中的其它数据。该标志位通常用于实时音频或视频流等应用程序。
当发送端设置 TCP 报文的 PSH 标志位为1时它是告诉接收端尽快将缓冲区中的数据交互给上层应用程序处理。这表明发送端希望接收端立即将数据传递给上层而不需要等待更多的数据或缓冲。
在缓冲区中有一个水位线的概念。当接收缓冲区中的数据达到水位线时读取操作就能够读取数据并返回给应用层若没有到达水位线读取操作可能会被阻塞等待数据到达水位线才能读取。 这些水位线的设置通常是由操作系统或网络库来管理以平衡数据传输的效率和延迟。具体的水位线值和行为可能因操作系统和网络库而异。
16位窗口大小 TCP 的接收缓冲区和发送缓冲区 每个 TCP Socket 在内核中都有一个发送缓冲区和一个接收缓冲区TCP 的全双工的工作模式以及 TCP 的流量拥塞控制都是依赖这两个独立的 buffer 的填充状态。
对于接收缓冲区当数据到达接收端的 TCP 连接时数据会被存储在接收缓冲区中即使应用程序没有调用 recv() 函数读取数据。无论应用程序是否读取数据对端发送的数据都会经过内核接收并缓存到接收缓冲区中。recv() 函数的作用是将内核缓冲区中的数据拷贝到应用层用户的缓冲区中并返回读取的字节数。对于发送缓冲区当应用程序调用 send() 函数发送数据时数据一般会被拷贝到发送端的内核发送缓冲区中然后 send() 函数会在上层返回。即 send() 函数返回时并不意味着数据一定发送到对端类似与写文件的行为。send() 函数仅将应用层缓冲区中的数据拷贝到 TCP 内核发送缓冲区中实际的发送过程由 TCP 协议负责。 数据在接收缓冲区和发送缓冲区之间的拷贝是通过操作系统内核实现的应用程序并不直接操作这些缓冲区。TCP 协议负责管理接收缓冲区和发送缓冲区的大小以及数据的读取和发送过程。 为什么要存在发送缓冲区和接收缓冲区存在的意义是什么 1️⃣ 发送缓冲区和接收缓冲区的存在可以实现发送端和接收端之间的解耦。发送端将数据放入发送缓冲区之后可以立即返回给应用程序而不需要阻塞等待数据发送到接收端。接收端也可以根据自身能力和需求从接收缓冲区中按需读取数据进行处理。
2️⃣ 发送/接收缓冲区的大小可以用于实现流控制机制。发送端的发送速率可能快于接收端的处理速率如果没有发送缓冲区发送端可能需要等待接收端的确认导致发送速率降低。发送缓冲区可以暂存数据让发送端可以连续发送一定量的数据。接收缓冲区可以通过控制接收端的处理速率。当接收端的处理能力有限时可以通过控制接收缓冲区的大小来限制发送端的发送数据保证数据的可靠传输。
3️⃣ 当网路发生拥堵时发送缓冲区可以暂存待发送的数据等待网路恢复时再进行正常发送从而减轻网络负载。 16位窗口 发送缓冲区和接收缓冲区的存在为窗口的出现提供了基础。TCP 窗口是一种流量控制机制用于控制发送端发送数据的大小以适应接收端的处理能力和网路条件。16位窗口大小表示的对端接收缓冲区的剩余空间大小。
接收端对发送端发来的数据进行响应时它会在确认消息中包含窗口大小字段告诉发送端当前自己接收缓冲区的剩余空间大小。发送端就可以根据窗口字段来调整自己发送数据的速度。
如果接收端的窗口字段较小表示接收端的接收能力较弱发送端应该减小发送数据的速度以避免造成数据丢失或拥塞。如果接收端的窗口字段较大表示接收端的接收能力较强发送端可以提高发送数据的速度。可充分利用网络宽带和接收端的处理能力。若窗口大小为0表示接收缓冲区已经被打满没有剩余的空间可以接收更多数据。此时发送端应该停止发送避免数据丢失。
确认应答ACK机制
TCP 中的确认应答机制是一种用于保证数据传输可靠性的机制。该机制确保发送方发送的数据能够被接收方正确接收在并要的时候进行重传。 确认应答机制的流程如下
发送方将数据分隔称为较小的数据段为每个数据段分配一个序列号以便接收方可以按序重组发送方将数据发送并启动一个定时器来跟踪每个数据段的发送时间接收方接收数据段响应 ACK 消息给发送方确认该数据段已经被正确接收发送方接收确认消息确认被接收端接收到的数据段。然后停止相应的定时器继续发送下一个数据段若发送方在定时器超时之前都没有接收到响应消息它会认为数据段可能丢失。因此发送端重传之前的数据段并且重启计时器接收方发送的响应消息也可能丢失。如果在一定时间内发送方没有收到响应消息它也会进行重传当接收段将数据报文接收完毕之后它将按照数据段的序列号进行数据重组。最后将重组成功之后的数据报文传递给应用程序。
数据接收方发送带有序列号的确认ACK以告诉发送方数据已被接收到指定的字节。ACK 并不意味着数据已经交互给应用程序它仅仅表示现在是接收方负责交互数据。
可靠性是通过发送方检测丢失的数据并重新传输来实现的。TCP 使用两种主要技术来识别丢失。超时重传RTO和重复累积确认DupAcks。
超时重传机制
在进行网络通信时。当发送方发送一个数据段后它会启动一个计时器等待接收方发送确认消息。如果在计时器结束之前没有收到确认消息那么发送方会认为发送的数据段已丢失然后将重新发送该数据段。
数据丢包分为两种情况第一种情况是发送方发送的数据段丢失了此时发送方如果在特定的时间内收不到响应报文就会进行超时重传。 主机A发送数据给主机B之后可能因为网络拥堵等原因数据无法到达主机B如果主机A在一个特定时间间隔内没有收到主机B发来的确认应答就会进行数据重发。
另一种情况是主机B对主机A发送的报文进行了响应但因为一些原因响应报文在网络中丢失了此时发送端也会因为收不到对应的响应报文而进行超时重传。 基于上面两种情况的数据丢包
当数据段丢失时发送方无法确认是发送的数据段丢失了还是对端响应的数据段丢失了。在这两种情况下发送方都无法收到对方的响应。因此发送方会进行数据段的超时重传若是因为接收端的响应报文丢失而导致的发送方的超时重传此时接收端就会收到很多重复的数据。那么 TCP 协议需要能够识别出哪些包是重复的包并且把重复的丢弃掉。这时我们就可以利用32位序列号来判断是否出现了重复的报文从而避免出现重复数据实现报文的去重
当发送方的数据段发送出去在没有收到确认报文之前。操作系统并不会立即将发送出去的数据报文从发送缓冲区中删除而是继续保留在发送缓冲区中直到发送方确认接受端已经准确接收到该报文时发送方才会将该报文从发送缓冲区中移除。这样就能确保在数据在网络传输中丢失之后能够进行重传。保证数据传输的可靠性。
那么超时的时间是如何确定的呢
最理想的情况下找到一个最小的时间保证 “确认应答” 一定能在这个时间内返回但是这个时间的长短随着网络环境的不同是会有差异的如果超时时间设置的太长了会影响整体的重传效率如果超时时间设置的太短了有可能会频繁的发送重复的包。
TCP 为了保证无论在任何环境下都能有比较高性能的通信确保数据的可靠传输会采用动态计算的方式来确定这个最大超时时间。
在 Linux 系统中BSD Unix 和 Windows 也是如此超时以 500ms 为一个单位进行控制每次判定超时重发的超时时间都是 500ms 的整数倍如果重发一次之后仍然得不到应答等待2倍的时间2500ms之后再一次进行重传。如果仍然没有收到确认响应会等待4倍的超时时间4500ms再进行重传以指数形式递增超时时间如果重传次数累计到一定的阈值TCP 会认为网络或对端主机出现异常情况会强制关闭连接以避免无限重传导致资源浪费和影响通信性能。
这种动态计算超时时间的机制会根据网络环境的不同自适应地调整超时时间以提供更好的性能和可靠性。通过指数递增的方式可以适应不同程度的网络延迟和丢包情况。 往返时间 RTTRound Trip Time 初始超时计时器的设置是基于往返时间RTT估计。其中初始定时器值是 RTTmaxG4*RTT 变化其中 G 是时钟粒度。这样可以防止由于故障或者恶意行为如中间人拒绝服务攻击者导致的过多传输流量。
准确的 RTT 估计对于丢失恢复非常重要因为它允许发送方在足够的时间过去后认为未确认的数据包已经丢失从而确定重新传输超时RTO - Retransmission Timeout的时间。重传歧义会导致发送方对 RTT 的估计不准确。在具有变化的 RTT 环境中可能会出现虚假超时如果 RTT 被低估那么 RTO 会触发不必要的重传或慢启动。在一次虚假的重传之后当原始传输的确认到达时发送方可能会错误地认为这些确认是对重传的确认并错误地得出结论即在原始传输和重传之间发送的数据段已经丢失导致进一步不必要的重传从而使链路真正拥塞。选择性确认可以减少这种效应。RFC 6298 规定在估计 RTT 时不得使用已经重传的数据段。Karn 算法确保只有在出现明确的确认之前才调整 RTO从而产生最终良好的 RTT 估计。然而在虚假重传之后可能需要很长时间才能获得明确的确认从而在此期间减低性能。TCP 时间戳也可以解决设置 RTO 时的重传歧义问题尽管它们不一定能提高 RTT 估计。
连接管理机制 TCP 是面向连接的如果理解连接呢 TCP 的可靠性机制是基于连接的并且与连接密切相关。TCP 通过建立连接来提供可靠的数据传输和其它的可靠性保证。
当一台服务器启动之后可能会有多个客户端同时连接而 TCP 的连接机制可以确保每个客户端与服务器之间都建立独立的连接。每个连接都有自己的接收缓冲区这样客户端发送的数据就可以被正确的接收和处理。 大量的连接操作系统就需要管理这些连接如何管理呢 当操作系统面对大量的连接时它需要能有效地管理这些连接以确保系统的性能。
操作系统通常会使用连接表Connection Table来管理连接。连接表是一个数据结构用于跟踪和储存当前活动的连接信息。每个连接都会在该数据结构中有一个对应的表项包含该连接的各项信息。通过连接表操作系统就可以快速检索和管理每一个连接。为了保证系统资源防止资源耗尽操作系统通常会对连接数进行限制。它可以设置最大连接数或使用其它策略来对连接的数量进行限制。操作系统需要监控连接的活动状态根据实际情况对超时和空闲连接进行相应管理。操作系统需要有效地处理并发的连接请求和数据传输。它可能使用多线程或多进程技术来处理并发的连接操作分配适当的资源和调度策略以提高系统的吞吐量和响应性能。
这些都是操作系统管理连接的一些机制实际的连接管理方式可能因为操作系统的不同而有所差异。
连接建立三次握手
在客户端与服务器建立连接之前服务器必须先绑定并监听一个端口以打开它供连接使用这称为被动打开passive open。一旦被动打开建立成功客户端可以通过启动主动打开active open来建立连接使用三次握手的方式 SYN客户端通过向服务器发送一个 SYN 来执行主动打开。客户端将数据段的序列号设置为一个随机值 XSYN - ACK服务器回复一个 SYN - ACK 作为响应。序列号设置为收到的序列号加1即 X1而确认号设置为收到的序列号加1即 Y1ACK最后客户端向服务器发送一个 ACK 。序列号设置为收到的确认号即 X1而确认号设置为收到的序号加1即 Y1
步骤1和步骤2建立并确认了一个方向客户端到服务器的序列号。步骤2和步骤3建立并确认了另一个方向服务器到客户端的序列号。完成这些步骤后客户端和服务器都已经收到了确认并建立了全双工通信。 为什么TCP建立连接是三次握手 1️⃣ 通过三次握手客户端和服务端都可以确认对方具备发送和接收数据的能力。在第一次和第二次握手中客户端和服务器都发送了 SYN 数据包对方收到后可以确认对方的接收能力。在第三次握手中客户端发送了 ACK 数据包服务器收到后可以确认客户端的发送能力。
2️⃣ 在网络中可能有已经失效的连接请求滞留在某个网络节点如果只有两次握手那么这些失效的连接会被误认为是真实连接浪费资源。而且两次握手容易遭受到攻击。
3️⃣ 三次握手是确保双方通信信道可靠性的最小次数。 TCP 三次握手时的状态变化 最初客户端和服务端都处于 CLOSE 状态服务端为了能够接收客户端的连接请求需要由 COLOSE 状态变为 LISTEN 状态等待客户端连接此时客户端就可以向服务器发起连接请求了当客户端发起第一次握手后客户端状态由 CLOSE 状态变为 SYN_SENT 状态处于 LISTEN 状态的服务器收到客户端的连接请求后就将该连接放入内核的等待队列中并向客户端发起第二次握手状态由 LISTEN 状态变为 SYN_RCVD 状态客户端收到服务器的第二次握手请求后向服务器发起第三次握手此时客户端的连接建立状态由 SYN_SENT 变为 ESTABUSHED服务器接收到客户端发来的第三次握手后服务器也建立连接成功此时服务器状态由 SYN_RCVD 变为 ESTABLISHED可以进行数据读写了。
连接终止四次挥手
连接终止阶段使用四次挥手进行连接的每一段独立地终止。 当一个端希望停止它那一半的连接时他发送一个 FIN 数据包另一端用 ACK 确认。因此典型的终止需要每个 TCP 端的一对 FIN 和 ACK 段。发送第一个 FIN 的一方用最后的 ACK 响应后在最终关闭连接之前等待一个超时在此期间本地端口不可用于新的连接这种状态允许 TCP 客户端在 ACK 在传输过程中丢失的情况下向服务器重新发送最终确认。超时的时间长度取决于实现但一些常见的值是30s、1min 和 2min。超时后客户端进入 CLOSE 状态本地端口可供新的连接使用。
我们以服务器和客户端为例当客户端与服务器通信结束时需要与服务器断开连接此时就不要进行四次挥手的过程
第一次挥手FIN客户端发送一个 FIN 报文给服务器表示客户端不会再发数据给服务器了第二次挥手ACK服务器接收到 FIN 报文后发送一个 ACK 报文给客户端表示已经收到了客户端的关闭连接请求第三次挥手FIN服务器发送一个 FIN 报文给客户端表示服务器也不会再向客户端发送数据了第四次挥手ACK客户端收到 FIN 报文后发送一个 ACK 报文给服务器表示已经收到了服务器的断开请求
在四次挥手中双方都直到彼此已经关闭连接并且可以安全地终止连接。
一些操作系统如 Linux 和 HP-UX实现了半双工关闭序列。如果主机主动关闭连接但仍有未读的传入数据可用主机发送信号 RST丢失任何接收到的数据而不是 FIN。这确保了 TCP 应用程序意识到数据丢失。
连接可以处于半打开状态在这种情况下一端终止了连接但另一端没有。终止连接的一端不能再向连接发送任何数据但另一端可以。终止来接的有胆应继续读取数据直到另一端也终止连。 TCP 四次挥手时的状态变化 在断开连接之前客户端和服务器都处于连接建立后的 ESTABLISHED 状态客户端主动调用 CLOSE 与服务器断开连接此时客户端状态由 ESTABLISHED 变为 FIN_WAIT_1服务器收到客户端发来的结束报文服务器返回确认报文状态由 ESTABLISHED 变为 CLOSE_WAIT客户端收到服务器对结束报文的确认 状态由 FIN_WAIT_1 变为 FIN_WAIT_2 开始等待服务器的结束报文段当服务器没有数据发送给客户端时服务器会向客户端发送 FIN 报文服务器状态由 CLOSE_WAIT 变为 LAST_ACK 等待最后一个 ACK 到来客户端收到服务器发来的结束报文段进入 TIME_WAIT 状态并发出最后一个确认报文 ACK服务器收到客户端发来的最后一个响应报文时服务器就会彻底关闭连接状态变为 CLOSE客户端在变为 TIME_WAIT 状态之后需要等待一个 2MSLMax Segment Life报文最大生存时间的时间才会进入 CLOSE 状态。
TCP 状态变化图如下所示 CLOSE 是一个假想的起始点不是真实的状态。
TIME_WAIT 状态
接下来进行一个测试首先启动 server 程序然后启动 client 程序。然后使用 Ctrl-C 终止 server 程序然后立即重启 server 程序如果如下 虽然 server 的应用程序终止了但 TCP 协议层的连接并没有完全断开因此不能再次监听同样的 server 端口。
使用 netstat 命令查看 TCP 协议规定主动关闭连接的一方要处于 TIME_WAIT 状态等待两个 MSLMaximum Segment lifetime的时间后才能回到 CLOSE 状态使用 Ctrl-C 终止 server 所以 server 是主动断开连接的一方在 TIME_WAIT 期间仍然不能监听一样的 server 端口MSL 在 RFC1122 中规定为 2min但是各个操作系统的实现不同在 Centos7 上默认配置的值为 60s如下所示通过命令 cat /proc/sys/net/ipv4/tcp_fin_timeout 查看 msl 的默认值。 为什么 TIME_WAIT 的时间是 2MSL 在 TIME_WAIT 状态下服务器会等待2倍的最大报文生存时间MSL这是为了确保网络中所有的延迟报文段都已经被丢弃。如果服务器立即关闭连接并重启可能会收到来自上一个连接的迟到报文这些报文可能数据错误的因此等待一段时间可以确保这些报文段已经被丢弃。
在 TIME_WAIT 状态下服务器仍然保持着连接状态以便接收可能迟到的最后一个 ACK 报文。如果最后一个 ACK 报文丢失服务器可以重发 FIN 报文即使客户端的进餐已经关闭TCP 连接仍然存在可以重发 LAST_ACK 报文。
通过设置 2MSL 作为 TIME_WAIT 的持续时间可以确保网络中的所有报文段都被丢弃防止连接混淆。 解决 TIME_WAIT 状态引起的 bind 失败的方法 在 server 的 TCP 连接没有完全断开之前不允许重新监听某些情况下是不合理的。
服务器需要处理大量的客户端连接请求每个连接的生存时间可能很短但是每秒都有大数量的客户端来请求这个时候如果服务器主动断开连接比如某些客户端不活跃就需要被服务器端主动清理掉就会产生大量的 TIME_WAIT 状态由于请求量很大就可能导致 TIME_WAIT 的连接数很多每个连接都会占用一个通信五元组源IP、源端口、目的IP、目的端口、协议。其中服务器的IP和端口和协议都是固定的。如果新来的客户端连接IP和端口号和 TIME_WAIT 占用的链接重复了就会出现问题。
因此当服务器端主动关闭连接时需要使用同样的端口立即重启。可以使用 setsockopt() 函数来设置套接字描述符的选项 SO_REUSEADDR 为1从而允许在相同的端口号上创建多个套接字描述符即使IP地址不同也可以。
#include sys/types.h
#include sys/socket.hint setsockopt(int sockfd, int level, int optname,const void *optval, socklen_t optlen);CLOSE_WAIT 状态
在 TCP 连接的四次挥手过程中如果客户端调用了 close 函数而服务器没有及时调用 close 函数关闭对应的文件描述符就会导致服务器进入 CLOSE_WAIT 状态而客户端则进入 FIN_WAIT_2 状态。但连接只有在完成四次挥手后才算真正断开并释放相应的连接资源。
如果服务器没有及时关闭不需要的文件描述符就会导致大量的连接处于 CLOSE_WAIT 状态每个连接都会占用服务器的资源。这会导致服务器的可用资源逐渐减少最终可能耗尽服务器的资源。
在这样的情况下不仅文件描述符泄漏还可能导致连接资源无法完全释放从而引发内存泄漏。因此在网络套接字程序运行之后如果服务器中有大量处于 CLOSE_WAIT 状态的连接就需要检测服务器有没有及时关闭文件描述符了。
总结对于服务器上出现大量的 CLOSE_WAIT 状态原因就是服务器没有正确的关闭 socket导致四次挥手没有正确完成。这是一个 BUG只需要加上对应的 close 即可解决该问题。
滑动窗口sliding window
之前说的确认应答策略对每一个发送的数据段都要给一个 ACK 确认应答收到 ACK 后再发送下一个数据段。这样做比较大的一个缺点就是性能较差。尤其是数据往返时间较长的时候。 既然这样一发一收的方式性能较低。那么我们采取一次发送多个数据段这样就可以大大的提高性能了即将多个数据段的等待时间重叠在一起了。 TCP 通信中虽然可以向对端一次性发送大量的报文但也不要将自己缓冲区中的数据一次性全部发送给对端也需要考虑对端的接收能力。 滑动窗口 TCP 滑动窗口可以确认一个主机发送给另一个主机的未确认字节数 x 。有两个因素决定 x 的值
发送主机上的发送缓冲区大小接收主机上的接收缓冲区的大小和可用空间。
发送主机不能发送超过接收主机的接收缓冲区可用空间的字节数。在接收主机的接收缓冲区中的字节被 TCP 确认之前发送主机的 TCP 必须等待发送更多的数据。
在接收主机上TCP 将接收到的数据存储在接收缓冲区中。TCP 确认接收到数据并向发送主机传达一个新的接收窗口大小。
发送缓冲区中的数据可以被分为以下三个部分 已经发送并且已经收到 ACK 的数据。这些是发送给接收方的数据并且接收方已经发送了确认应答。这部分数据可以从发送缓冲区中删除了。已经发送但还没有收到 ACK 的数据 。这些数据已经发送给接收方但还没有收到确认应答。这部分数据仍然需要留在发送缓冲区中因为发送方需要在超时或接收方重新请求时进行重传。待发送的数据。发送方准备发送但还没有发送的数据。这些数据等待发送直到发送窗口允许发送。
这样可以更好的帮助发送方跟踪数据的状态保证数据的可靠传输。 窗口大小指的是无需等待确认应答而可以继续发送数据的最大值下图的窗口大小就是 4000 字节四个数据段。发送前四个数据段时不需要等待任何的 ACK 直接发送。收到第一个 ACK 后滑动窗口向右移动继续发送第五个数据段以此类推。操作系统为了维护这个滑动窗口需要开辟 发送缓冲区 来记录当前还有哪些数据没有应答。只有确认应答过的数据才能从缓冲区中移除掉。窗口越大则网络的吞吐量越高。 滑动窗口存在的意义在于可以提高发送数据的效率和数据可靠性传输的保证
假设发送方不考虑拥塞窗口并且对端的窗口的接收能力一直为4000字节。根据滑动窗口原理发送方可以连续发送多个数据段直到滑动窗口的大小到达4000字节为止。这样就充分利用了网络宽带提高了数据发送的效率。如上图所示发送方依次发送了编号 1001 ~ 2000、2001 ~ 3000、3001 ~ 4000、4001 ~ 5000 的四个数据段而无需等待对方的 ACK 应答。当发送方收到对方的确认序号2001时说明 1001 ~ 2000 这个数据段已经被对方成功接收。此时滑动窗口向右移动1000个字节让滑动窗口的左边界为2001即将该段数据移除掉。然后将滑动窗口的右边界向右移动1000个字节表示将 5001 ~ 6000 的数据段发出。以此类推。滑动窗口的大小决定了发送方可以连续发送的数据量从而影响数据的传输能力。较大的滑动窗口可以提高网路的吞吐量同时也反应了对端的接收能力。滑动窗口不仅收到对方窗口的限制还受到网络拥塞控制机制的影响。实际上需要综合考虑对端窗口大小、网络拥塞情况以及其它影响因素动态调整滑动窗口的大小以实现最有的传输效果。
TCP 的重传机制要求保持发出但未收到确认应答的数据这部分数据就位于滑动窗口中滑动窗口左侧的数据已被发出确认可以移除。滑动窗口中之所以要保存发出但未收到确认的数据是为了支持 TCP 的重传机制。 丢包问题 如果出现了数据丢包如何进行重传这里分两种情况讨论。
情况一数据包已抵达ACK 丢包了。 发送端发送多个数据段时部分 ACK 丢失并不会导致问题因为后续的 ACK 可以确认应答。
上图中1 ~ 1000、2001 ~ 3000、3001 ~ 4000 的数据段丢失了但当发送端收到最后 5001 ~ 6000 数据段的响应此时发送端就确认之前的数据段都收到了。因为如果接收方没有收到 1 ~ 1000、2001 ~ 3000、3001 ~ 4000 的数据段是不会设置确认序号为6001的确认序号的意思就是该确认序号之前的数据都已经收到。
情况二数据包丢失。 当某一段报文段丢失后发送端会一直收到 1001 这样的 ACK表示这段报文没有收到如果发送端主机连续收到三次同样一个 “1001” 这样的应答就会将对应的数据 1001~2000 重新发送这是如果对端接收到了 1001~2000 的报文之后再次返回的 ACK 就是 7001 了因为 2001 ~ 7001 接收端在之前已经收到了被放到接收端操作系统内核的接收缓冲区中了。
这种机制被称为 “高速重发机制快重传 - Fast Retransmit”。 快重传 VS 超时重传 快速重传Fast Retransmit和超时重传Timeout Retransmit是 TCP 中两种常见的重传机制都用于处理数据丢包的情况但它们触发的条件有所不同。
1️⃣ 快速重传
触发条件当发送方连续收到相同的重复 ACK 时可以推断出前一个数据段丢失。行为发送方立即进行重传而无需等待超时定时器触发。它可以快速的重传丢失的数据报文提高数据传输的速率。
2️⃣ 超时重传
触发条件当发送方发送数据后在定时器超时之前没有收到 ACK 确认信息可以认为数据报文丢失。行为定时器超时后发送方将丢失的数据进行重传。超时重传时间相对较长适用于网络延迟较高或不稳定的情况。
实际中TCP 会结合两种重传机制以适应不同的网络环境和数据丢失情况。快速重传可以提供更快的重传响应时间而超时重传可以作为一种保底机制用于处理更严重的数据丢失情况。
流量控制Flow control
TCP 使用端到端的流量控制协议来避免数据发送方发送数据的速度过快导致 TCP 接收方无法可靠地接收和处理数据。在不同网络速度的机器之间进行网络通信时流量控制机制至关重要。例如如果PC向智能手机发送数据而智能手机正在缓慢地处理接收到的数据智能手机必须能够调节数据流以避免被数据淹没。
TCP 使用滑动窗口流量控制协议。在每个 TCP 段中接收方在接收窗口的字段中指定它愿意为连接缓冲的额外接收数据量以字节为单位。发送主机只能发送一定数量的数据然后必须等待接收主机的确认并接收窗口更新。
当接收方宣布窗口大小为0时发送方停止发送数据并启动持续计时器persist time。持续计时器的目的是保护 TCP 免受死锁状态的情况如果接收方后续的窗口大小更新丢失发送方无法发送更多的数据。当持续计时器到期时TCP 发送方尝试发送一个小数据包来恢复以便接收方通过发送另一个含新窗口大小的确认来响应。
如果接收方以很小的增量处理传入数据它可能会重复通告一个较小的接收窗口。这被称为愚蠢窗口综合征silly window syndrome因为在 TCP 段中只发送几个字节的数据是低效的TCP 报头的开销相对较大。
即接收到处理出数据的能力是有限的。如果发送端发送数据过快导致接收端的接收缓冲区被打满则时候如果发送端继续发送就会造成丢包继而引起一系列的丢包重传等一系列的连锁反应。因此 TCP 支持根据接收端的处理能力来决定发送端的发送速度这个机制就叫做 流量控制Flow Control。
接收端将自己可以接收的缓冲区大小放入 TCP 首部中的 “窗口大小” 字段通过 ACK 端通知发送端窗口大小字段越大说明网络的吞吐量越高接收端一旦发现自己的缓冲区快满了就会将窗口大小设置成一个更小的值响应给发送端发送端接收并提取到这个响应中的窗口大小时就会按照窗口具体大小来调整自己的发送速度如果接收端缓冲区满了就会将窗口设置为0这是发送方不再发送数据但需要定期发送一个窗口探测数据段使接收端将窗口大小告诉发送端。 接收端将窗口大小信息告知发送端是通过 TCP 首部中的16位窗口字段实现的。那么问题来了16位数字最大表示65535那么 TCP 窗口最大就是65535字节吗
实际上TCP 首部40个字节选项中包含了一个窗口扩大因子Window Scale OptionM这个选项用于扩大窗口大小的表示范围。窗口扩大因子是一个8位的字段它指示了窗口字段的有效位数。通过窗口扩大因子可以将窗口字段的值左移 M 位从而实现更多大的窗口大小。示例如果窗口字段的值为1000窗口扩大因子 M2那么实际窗口大小就是1000左移两位即4000字节。
拥塞控制Congestion control
虽然 TCP 有了滑动窗口这个杀器能够高效可靠的发送大量的数据。但是如果在刚开始阶段就发送大量的数据仍然可能引发问题。虽然网络上有很多的计算机可能当前的网络状态就已经比较拥堵。在不清楚当前网络状态情况下贸然发送大量的数据可能雪上加霜。
TCP 使用多种机制实现高性能并避免拥塞崩溃Congestive Collapse即网络性能严重降低的死锁状态。这些机制控制着进入网络的数据速率使数据流量保持在不会引发崩溃的速率以下。它们还在流量之间产生近似最大最小的公平分配。
数据发送时确认或未确认的情况被发送方用来推断 TCP 发送方和接收方之间的网络状况。结合定时器TCP 发送方和接收方可以改变数据流的行为。这通常被称为拥塞控制或拥塞避免。
TCP 引入 慢启动 机制先发送少量数据探探路摸清当前网络拥堵状态再决定按照多大的速度传输数据。 此处引入一个概念为 拥塞窗口。拥塞窗口是 TCP 中用于控制发送速率的一个关键参数。发送开始时拥塞窗口大小被初始化为1。每次收到一个 ACK 应答拥塞窗口的大小就会增加1每次发送数据包的时候发送方会将拥塞窗口的大小与接收端主机反馈的窗口大小进行比较然后选择较小的值作为实际发送窗口的大小。
像说明这样的拥塞窗口增长速度是指数级别的“慢启动” 只是指初始时慢但是增长速度非常快。
为了不增长的那么快因此不能使拥塞窗口单纯的加倍此处引入一个叫做慢启动Slow Start的阈值Threshold初始阈值被设置为一个较小的值通常是网络的初始拥塞窗口大小当拥塞窗口超过这个阈值的时候不再按照指数方式增长而是按照线性方式增长 当 TCP 开始启动的时候慢启动阈值被设置为拥塞窗口的最大值当发送超时重传时慢启动阈值会被设置为原来阈值的一半并将拥塞窗口重新置为1。这样做的目的是降低发送速率以避免进一步加重网络的拥塞情况。
在 TCP 通信中少量的丢包通常被视为随机的噪声而不是网络拥塞的指示。因此发送少量丢包时TCP 会通过触发超时重传来进行恢复即重新发送未确认的数据。
当出现大量丢包时TCP 会认为是网络拥塞的迹象。为了应对拥塞TCP 会采取响应的措施来减缓发送速率避免进一步加重网络拥塞。
在 TCP 通信开始后网络吞吐量会逐渐上升随着网络发送拥堵吞吐量会立刻下降。拥塞控制其实就是 TCP 协议想尽可能快的把数据传输给对方但是又要避免给网络造成太大压力的折中方案。
延迟应答Delayed Acknowledgment
如果接收数据的主机立刻返回 ACK 应答这是返回的窗口可能比较小。
假设接收端缓冲区为 1M。一次收到了 500K 的数据如果立即应答返回的窗口就是 500K但实际上可能处理端处理数据的速度非常快10ms 之内就把 500K 数据从缓冲区拿走处理了在这种情况下接收端处理还没有达到自己的极限及时窗口再放大一些也能处理过来如果接收端稍微等一会在应答比如等待 200ms 再应答那么这时候返回的窗口大小就是 1M
窗口越大网络吞吐量就越大传输效率就越高。我们的目标就是在保证网络不拥塞的情况下尽量提高传输效率。 那么所有的包都可以延迟应答么肯定也不是。
数量限制每隔 N 个包就应答一次。当接收端收到 N 个包后会一次性发送一个 ACK 应答而不是针对每个包都发送 ACK 应答。
时间限制超过最大延迟时间就应答一次。
具体的数量限制和超时时间可能因操作系统的不同而有所差异。一般情况下常见的设置是每隔2个包发送一次 ACK 应答并设置最大延迟时间为 200ms 。在减少 ACK 应答的同时尽量保持对发送端的及时反馈。
捎带应答
在应用层进行一发一收的交互时可以利用 ACK 应答与应用层的数据回复进行优化。当客户端向服务器发送一条请求例如 “How are you?”服务器在处理完请求之后会给客户端回复一条应答 “Fine, thank you!”。这个时候 ACK 可以搭顺风车和服务器回应的消息一起回给客户端。 服务器在发送应答数据给客户端时可以在 TCP 层同时发送 ACK 应答。这样客户端接收到应答数据的同时也会收到 ACK 应答从而减少额外的 ACK 传输。
通过将 ACK 应答与应用层数据恢复进行合并可以减少网络上的额外数据传输提高整体的传输效率和响应速度。
面向字节流
创建一个 TCP 的 socket 时内核会同时创建一个 发送缓冲区 和 接收缓冲区。
当调用 write 时数据会先写入发送缓冲区中如果写入的字节数超过一个 TCP 数据包的大小数据会被拆分成多个 TCP 数据包进行发送如果写入的字节数较少数据会在发送缓冲区中等待等到缓冲区长度差不多了或者其它合适的实际发送出去接收数据的时候数据也是从网卡驱动程序到达内核的接收缓冲区然后应用程序可以调用 read 从接收缓冲区中读取数据TCP 的一个连接既有发送缓冲区和接收缓冲区那么对于这一个连接既可以读取数据也可以写入数据。这使得连接能够进行全双工通信。
由于缓冲区的存在TCP 程序的都和写都不需要一一匹配例如
写100个字节数据时可以调用一次 write 写100个字节也可以调用100次 write 每次写一个字节读100个字节数据时也完全不需要考虑写得时候是怎么写的既可以一个 read 100个字节也可以一次 read 一个字节重复100次。
读和写操作的灵活性使得应用程序可以根据自身的需求和逻辑进行数据的读写操作。应用程序可以按照自己的设计和实现选择适合的读写方式不需要与之前的写入一一对应。
粘包问题
首先要明确在粘包问题中“包” 指的是应用层的数据包在 TCP 的协议报头中没有像 UDP 一样的 “报文长度” 字段但是有一个序号这样的字段从传输层的角度看TCP 是一个一个报文过来的按照序号排好序放在缓冲区中从应用层的角度看看到的只是一串连续的字节数据因此应用程序看到了这么一连串的字节数据就不知道从哪个部分开始到哪个部分是一个完整的应用层数据包。
即 TCP 中的粘包问题是指发送方连续发送的多个数据包在接收方接收时被看作一个数据包的现象。这可能会导致接收方无法准确地识别每隔数据包的边界从而造成数据解析错误。
✨那么如何避免粘包问题呢解决问题的本质就在于明确两个包之间的边界。
定长包对于固定长度的包可以简单地按照固定长度大小读取每个包。例如在 Request 结构中大小是固定的可以从缓冲区的开头开始按照 sizeof(Request) 依次读取即可变成包长度字段对于变长的包可以在包头的位置约定一个字段来表示整个包的长度从而确定包的结束位置。在读取时先读取长度字段然后根据长度读取对应数量的数据以确保包的完整性变长包分隔符可以在包和包之间使用明确的分隔符。应用层协议可以根据自己的需要来定义分隔符只要确保分隔符不会与正文数据冲突即可。在读取时可以根据分隔符将缓冲区拆分为多个完整的包。
☑️ 对于 UDP 协议来说, 是否也存在 “粘包问题” 呢?
对于 UDP如果还没有上层交付数据UDP 的报文长度仍然在。同时UDP 是一个一个把数据交互给应用层就有很明确的数据边界从应用层的角度来看使用 UDP 时数据包的交付给应用层是一个一个进行的这意味着应用层要么收到完整的 UDP 报文要么不收不会出现 “半个” 的情况。
TCP 异常情况
在 TCP 中存在一些异常情况可能会导致连接的中断或异常终止。如下是一些常见的 TCP 异常情况
进程终止当 TCP 连接相关的进程终止时操作系统会释放相关的文件描述符并发送一个 FIN 段给对端。这和正常关闭没有什么区别。机器重启当机器重新启动时TCP 连接相关的进程也会被关闭。此时与进程终止情况类似操作系统会释放文件描述符并发送 FIN 段给对端。机器断电/网线断开接收端认为连接还在一旦接收端有写入操作接收端发现连接已经不在了就会进行 reset。即使没有写入操作TCP 自己也内置了一个保活定时器会定期向对方发送保活消息检测对端是否还在。如果对端不在了也会把连接释放。应用层检测机制某些应用层协议也可能具有自己的连接状态检测机制。例如在 HTTP 长连接中会定期发送心跳检测请求以检测对方的状态。类似地一些即使通讯应用如 QQ 在断线后也会尝试定期重新连接。
基于 TCP 应用层协议
下面是几种基于 TCP 的常见应用层协议
HTTP超文本传输协议HTTPS安全超文本传输协议SSH安全外壳程序Telnet远程终端协议FTP文本传输协议SMTP简单邮件传输协议
除了上述列举的常见应用层协议外还可以根据具体需求自定义开发 TCP 应用层协议以满足特殊场景的需求。
TCP 小结
TCP 协议之所以复杂是因为它需要在保证可靠性的同时尽可能提高性能。
可靠性
校验和检测数据在传输过程中是否发生了错误以保证数据的完整性。序列号通过序列号对数据进行编号确保数据按序到达目的地。确认应答接收方收到数据会发送确认应答告知发送方数据已成功接收。超时重发发送方发送数据后设置一个定时器如果在规定时间内未收到确认应答会重新发送数据。连接管理TCP 在建立连接和断开连接时进行握手和挥手确保通信双方的状态同步和可靠连接的建立与关闭。流量控制使用滑动窗口机制来控制对方发送数据的速率以避免接收方缓冲区溢出。拥塞控制通过拥塞窗口和拥塞避免算法来控制网络中的拥塞情况以避免网络过载和性能下降。
提高性能
滑动窗口用来实现流量控制和提高传输效率允许发送方连续发送多个数据包而无需等待确认应答。快速重传若接收方收到了乱序的数据包或发生丢包会立即发送重复确认通知发送方进行快速重传避免等待超时重传的时间延迟。延迟应答接收方可以在一定时间内等待多个数据包然后一次性发送确认应答以减少网络中的确认报文数量。捎带应答允许在发送确认应答时携带之前未确认的数据包的确认信息减少确认报文的数量和网络延迟。
其它
定时器TCP 使用不同类型的定时器来处理超时重传、保活和 TIME_WAIT 等情况以确保通信的稳定性和安全性。