五、流量控制机制
5.1 流量控制的简单介绍
接收端处理数据的速度是有限的。如果发送端发的太快,导致接收端的缓冲区被打满,这个时候如果发送端继续发送,就会造成丢包,继而引起丢包重传等一系列连锁反应。
因此TCP支持根据接收端的处理能力, 来决定发送端的发送速度. 这个机制就叫做流量控制(Flow Control);
提示:从宏观上讲,对缓冲区的读取和写入本质上就是一个生产消费模型,所谓的流量控制就是对发送行为的同步过程。
- 接收端将自己可以接收的缓冲区大小放入 TCP 首部中的 “窗口大小” 字段, 通过ACK通知发送端;
- 窗口大小字段越大, 说明网络的吞吐量越高;
- 接收端一旦发现自己的缓冲区快满了, 就会将窗口大小设置成一个更小的值通知给发送端;
- 发送端接受到这个窗口大小之后, 就会减慢自己的发送速度;
- 如果接收端缓冲区满了, 就会将窗口置为0;这时发送方暂时不再发送数据。
- 如果接收端处理了缓冲区中的一部分数据,就会主动向发送端发送窗口更新通知。
- 如果发送端在一段时间内没有收到接收端的窗口更新通知,就需要定期发送一个窗口探测数据段, 使接收端把窗口大小告诉发送端.
TCP窗口大小的最大值
- 在TCP首部中,窗口大小是一个16位的字段,最大值是64KB,难道TCP的窗口大小最大只有64KB吗?当然不是!
- 事实上,TCP首部的40字节选项中还包含了一个窗口扩大因子M, 实际窗口大小是:窗口字段的值左移 M 位;
首次发送如何得知接收端的窗口大小?
- 在三次握手建立连接的过程中:
- 建立可靠的连接
- 协商起始序号
- 交换双方的接收缓冲区大小
- 也就是说在正式通信发送数据之前,双方就已经知道了对方的窗口大小了。
提示:实际上在第三次握手时,客户端就已经知道了服务端的窗口大小。所以第三次发送的ACK报文是可以携带数据的,即捎带应答
5.2 滑动窗口
TCP的流量控制机制主要通过滑动窗口来实现的。
5.2.1 允许连续发送多个报文
滑动窗口机制允许发送方在收到接收方的确认应答之前,连续发送多个TCP报文段,而不是每次只发送一个报文段并等待确认。
一发一收的方式性能较低,那么我们一次发送多条数据,就可以大大的提高性能(其实是将多个段的等待时间重叠在一起了)。
5.2.2 滑动窗口的工作原理
发送缓冲区的划分
参照滑动窗口算法(同向双指针),我们可以用三个指针(窗口的左右指针+有效数据的边界)将发送缓冲区分成以下四个部分:
-
已发送已应答(可覆盖,即从发送缓冲区中移除)
-
允许发送未应答(滑动窗口)
-
未发送未应答
-
缓存区的剩余空间
发送窗口(滑动窗口):发送方维护一个发送窗口,该窗口的大小取决于接收方的接收能力(即接收窗口大小)和网络的拥塞状况。发送窗口内的数据是允许发送但尚未收到确认应答的数据,也就是说窗口内的数据是可以一次连续发出而中间不需要等待确认的。
窗口滑动
- 滑动窗口内的数据段是可以一次连续发出的,不必每发送一个报文段就等待确认应答。
- 当发送方收到接收方对2000报文段的确认应答后(2001),发送窗口会向右滑动,已发送并收到确认应答的报文段不再保留在窗口中,同时会有一个新的数据段被包含进窗口中,继续发送新数据段。
- 发送窗口(滑动窗口)越大,网络的吞吐量就越大,传输效率就越高。
滑动窗口的右移和大小的变化完全是由其左右指针控制的:
- left = 确认序号(标记的位置)
- right = 确认序号 + min(接收窗口大小,拥塞窗口大小)
注意:
- right指针不会超过接收缓冲区中有效数据的边界。
- 确认序号和接收窗口的大小从接收端发送的ACK报文中获知。
- 拥塞窗口的大小是由发送端的拥塞控制机制探测得到。(后面会讲)
滑动窗口在发送缓冲区中不断右移时会发生越界吗?
- 当然不会,tcp的发送缓冲区采用了类似循环队列的算法,以确保滑动窗口不会越界。
5.2.3 丢包的处理机制
情况一: 数据包已经抵达, ACK报文丢失
- 根据确认序号的定义:确认序号之前的数据已经被成功接收。因此即使部分ACK丢失,也会有后续的ACK做统一确认。
- 如上图:确认序号为1001,3001,4001的ACK报文丢失,但是只要发送端接收到确认序号为6001的报文,滑动窗口的左指针就会直接移动到6001的位置,继续下一轮次的发送。
- 那如果是发送窗口中的最后一个报文段的ACK丢失呢?别忘了我们还有超时重传机制,超过一定时间后发送端会重发数据并收到ACK,接收端需要对报文进行去重。
情况二:数据包丢失
- 根据确认序号的定义:确认序号之前的数据已经被成功接收。当1001~2000报文段丢失之后, 发送端会一直收到确认序号是1001的ACK报文, 就像是在提醒发送端 "我想要的是 1001"一样。此时发送窗口的左指针会一直停在1001的位置。
- 如果发送端主机连续三次收到了同样一个 “1001” 这样的应答, 就会将对应的数据 1001 - 2000 重新发送。这种机制被称为高速重发控制,也叫快重传。
- 这个时候接收端收到了1001~2000之后, 再次返回的ACK就是7001了(因为2001 - 7000)接收端其实之前就已经收到了, 被放到了接收端操作系统内核的接收缓冲区中。
- 发送端收到7001的确认序号后,将发送窗口的左指针直接移动到7001所标记的位置。
- 那如果是发送窗口临近末尾的几个数据段发生丢失呢?或是失序应答报文也发生丢失,使得发送端无法收到三次同样的失序应答进而激活快重传机制呢?别忘了我们还有超时重传机制,超过一定时间后发送端会重发数据并收到ACK。
小结:确认序号保证了滑动窗口连续的向后移动更新,不会出现跳跃的情况
5.3 流量控制的实现过程
- 初始状态:
- 发送方和接收方三次握手建立连接时,会协商初始序列号和初始窗口大小(接收窗口)。
- 数据发送:
- 发送方根据发送窗口的大小,连续发送多个报文段。
- 在发送过程中,发送方会根据ACK报文的确认序号和窗口大小字段不断移动发送窗口,更新发送窗口的大小,以确保不会超出接收方的接收能力。(在不超出接收方的接收能力的情况下尽可能地提高传输效率)
- 同时,对发送过程中的丢包问题,发送方也有对应的快重传和超时重传机制进行补发。
- 确认应答:
- 接收方在收到数据后,会向发送方发送确认应答(ACK),以告知已接收到的数据范围(确认序号)和接收窗口的大小。
- 窗口调整:
- 发送方在收到确认应答后,会更新发送窗口的位置和大小,继续发送新的数据。
- 发送方会根据接收方的接收能力和网络的拥塞状况,动态调整发送窗口的大小,以确保不会发送过多的数据导致接收方无法处理。
5.4 流量控制的优点
- 提高传输效率:通过允许发送方连续发送多个报文段,减少了等待确认应答的时间,从而提高了数据传输的效率。
- 防止数据丢失:通过控制发送方的发送速率,避免了因发送过快而导致接收方无法及时处理数据引起的数据丢失问题。
综上所述,TCP的流量控制机制通过滑动窗口实现了对发送方发送速率的控制,确保了数据传输的可靠性和效率。
六、拥塞控制机制
TCP(传输控制协议)的拥塞控制机制是为了避免过多的数据同时被发送到网络中,从而防止网络拥塞。TCP使用一系列算法来动态调整数据传输的速率,以适应网络中的实际拥塞状况。
流量控制机制考虑的是接收方的接收能力,而拥塞控制机制考虑的是网络信道的承载能力。
归根结底二者都是调整数据传输速率的机制,底层的实现原理最终都是通过控制滑动窗口标定允许直接发送的数据区域从而动态调整数据传输的速率。
流量控制机制通过接收方ACK报文中的确认序号和接收窗口大小来控制发送窗口的更新(位置移动和大小变化)。
拥塞控制机制则要引入一个新的概念:拥塞窗口,表示发送端在某一时刻可以发送的最大数据量,用于防止网络拥塞。
6.1 慢启动
-
目的:在TCP连接开始时,逐渐增加发送方的发送速率(拥塞窗口大小),以避免一开始就过载网络。
-
实现方式:
- 初始拥塞窗口大小通常设置为几个最大段大小(MSS)一般为1个MSS。
- 每当收到一个ACK(确认应答),拥塞窗口就增加一个MSS,这意味着每个RTT(往返时间)拥塞窗口大小加倍,呈指数增长。这样可以快速地增加网络的使用效率。
- 当拥塞窗口大小达到慢启动阈值(ssthresh)后,TCP进入拥塞避免阶段。
注意:拥塞窗口的增长速度是指数级别的,所谓的慢启动指的是开始阶段增长慢,是为了探探路,摸清当前的网络拥堵状态。如果网络信道通畅,之后就快速增长,是尽快恢复正常通信。
6.2 拥塞避免
-
目的:在拥塞避免阶段,控制拥塞窗口的增长速度,以避免因增长速度过快而导致的网络拥塞。
-
实现方式:
- 当拥塞窗口大小超过慢启动阈值后,每个RTT拥塞窗口增加1个MSS,呈线性增长,逐步增加流量以找到网络的最佳承载能力。
注意:
- 网络拥塞不是通信双方出现的问题,而是信息的传输介质“网络”出现的问题。通常是由于硬件设备出故障或是数据量太大而引起的阻塞或延迟问题。
- 因此在发生网络拥塞时,网络中所有检测到拥塞的TCP/IP协议主机都要通过慢启动和拥塞避免机制迅速减少数据传输的速率,使得拥塞的数据在网络中尽快消散,恢复网络信道的通畅。之后各主机再分别恢复数据传输速度。
6.3 丢包的处理机制
当发生丢包时,TCP协议就认为当前的网络信道存在拥塞问题:
6.3.1 超时重传–>慢启动
- 如果丢包通过计时器超时被检测到,TCP会进行超时重传。
- 同时,TCP会将慢启动阈值(ssthresh)设置为当前拥塞窗口大小的一半,这是一种平衡网络拥塞和传输效率的动态调整策略。如果当前窗口已经很小(拥塞严重),那么对应的慢启动阈值会更小,之后就会很快的进入拥塞避免阶段,缓慢增加拥塞窗口的大小以减少网络负担。如果当前窗口很大(拥塞不严重),那么对应的慢启动阈值也会较大,拥塞窗口会在慢启动阶段指数增长,迅速恢复传输速度,避免出现因窗口过度减小而导致的性能损失。
- 此外,TCP会将拥塞窗口重置为初始值(通常为1个报文段),重新进入慢启动阶段。这是因为超时重传通常意味着网络发生了严重拥塞,所以需要迅速减小数据发送速率来缓解网络负载。
6.3.2 快速重传–>快速恢复
- 如果丢包通过重复的失序ACK报文被检测到,TCP会进行快速重传。这意味着发送端会立即重传丢失的数据包,而不是等待超时事件。
- 同时,TCP会将慢启动阈值(ssthresh)设置为当前窗口大小的一半,但不会将拥塞窗口(cwnd)重置为初始值。
- 相反,TCP会将cwnd减小为ssthresh的大小后开始线性增长,即直接进入拥塞避免阶段,而不会重新进入慢启动阶段。这种机制就叫做快速恢复。这是因为快速重传通常表明网络发生了较轻微的拥塞,而不是严重的网络问题。因此,TCP通过减小拥塞窗口,但保持在拥塞避免阶段,来快速恢复数据传输速度,而不需完全重新开始慢启动。
为什么存在慢启动和快速恢复两种解决方案?
- 如果是超时重传,表示接收端静默了较长的一段时间,那么大概率此时的网络存在严重的拥塞,需要慢启动缓解网络负载。
- 而如果是快重传,虽然也是发生了丢包,但是接收端能不断应答失序报文,说明此时的网络发生了较轻微的拥塞,通过快速恢复适量的减小窗口(1/2),可以在缓解网络负载的同时也快速恢复数据的传输速度。
七、延迟应答机制
TCP协议的延迟应答机制是TCP用于提高网络传输效率的一种重要手段。以下是对该机制的详细解释:
延迟应答机制的定义
延迟应答机制指的是,在TCP通信中,当接收端(Receiver)收到发送端(Sender)发送的数据后,并不会立即返回ACK(确认应答)报文给发送端,而是会等待一段时间(这个时间可以是程序员设定的,也可以是TCP协议栈内部默认的)后再发送ACK报文。这样做的目的是为了让接收端在这段时间内能够释放一部分接收缓冲区的空间,从而允许发送端在后续的发送过程中拥有更大的窗口大小,进而提高网络的吞吐量和传输效率。
延迟应答机制的作用
- 提高传输效率:通过延迟应答,接收端能够释放更多的接收缓冲区空间,使得发送端在后续的发送过程中可以拥有更大的窗口大小,从而发送更多的数据,提高网络的吞吐量。
- 减少网络拥塞:在网络拥塞的情况下,延迟应答机制可以减少ACK报文的发送频率,从而降低网络负载,有助于缓解网络拥塞。
延迟应答机制的限制
虽然延迟应答机制能够提高网络传输效率,但它也存在一定的限制。这些限制主要包括:
- 数量限制:在某些情况下,TCP协议会规定每隔N个数据包就应答一次,而不是对每个数据包都进行应答。这样做可以减少ACK报文的数量,降低网络负载。一般情况下N取2。(如上图)
- 时间限制:为了避免过长的延迟导致发送端超时重传,TCP协议还会设置一个最大延迟时间。如果在这个时间内接收端仍然没有发送ACK报文,那么发送端就会认为数据包丢失,从而触发超时重传机制。一般情况下,最大延迟时间取200ms。
延迟应答机制的实现
在TCP协议的实现中,延迟应答机制通常是由TCP协议栈内部自动完成的,无需应用程序进行干预。但是,程序员可以通过调整TCP协议栈的相关参数来影响延迟应答机制的行为,比如设置最大延迟时间、调整ACK报文的发送频率等。
提示:我们在编写网络程序时,应该使用read/recv函数尽快把数据从内核中的接收缓冲区中读走,以提高网络传输的效率。
八、捎带应答机制
TCP协议的捎带应答机制是一种基于延迟应答机制进一步优化的网络通信效率提升手段。以下是关于TCP协议捎带应答机制的详细解释:
捎带应答机制的定义
捎带应答机制是指在TCP通信中,当接收端(Receiver)收到发送端(Sender)发送的数据后,并不立即返回ACK(确认应答)报文,而是等待一段时间(这段时间可能由于延迟应答机制而延长)。如果在这段时间内,接收端需要向发送端发送其他响应数据(如应用层的数据响应),则可以将ACK报文与应用层响应数据合并为一个报文发送给发送端,这个过程就被称为捎带应答。
捎带应答机制的作用
- 提高传输效率:通过捎带应答,可以减少网络中传输的报文数量。原本需要单独发送的ACK报文可以与应用层响应数据合并发送,从而减少网络负载,提高传输效率。
- 减少网络延迟:由于减少了报文数量,网络中的传输延迟也会相应减少。发送端和接收端之间的交互更加紧凑,有助于提升整体通信性能。
捎带应答机制的实现
捎带应答机制的实现依赖于TCP协议栈的内部处理逻辑。当接收端收到发送端的数据时,TCP协议栈会检查是否有需要发送的响应数据。如果有,并且满足捎带应答的条件(如延迟时间未超过最大限制),则TCP协议栈会将ACK报文与响应数据合并为一个报文发送给发送端。
捎带应答机制的注意事项
- 不是所有情况都适用:捎带应答机制并不是在所有情况下都适用。它依赖于接收端是否有需要发送的响应数据,以及延迟时间是否允许合并报文。
- 避免过度延迟:虽然延迟应答机制有助于提高传输效率,但过度的延迟可能会导致发送端超时重传,从而降低通信性能。因此,在设置延迟时间时需要权衡效率和可靠性。
九、面向字节流
创建一个TCP的socket,会同时在内核中创建一个发送缓冲区和一个接收缓冲区;
- 调用write时, 数据会先写入发送缓冲区中;
- 如果发送的字节数太长, 会被拆分成多个TCP的数据包发出;
- 如果发送的字节数太短, 就会先在缓冲区里等待, 等到缓冲区长度差不多了, 或者其他合适的时机发送出去;
- 接收数据的时候, 数据也是从网卡驱动程序到达内核的接收缓冲区;
- 然后应用程序可以调用read从接收缓冲区拿数据;
- 另一方面, TCP的一个连接, 既有发送缓冲区, 也有接收缓冲区, 那么对于这一个连接, 既可以读数据, 也可以写数据. 这个概念叫做全双工
由于缓冲区的存在, TCP程序的读和写不需要一一匹配, 例如:
- 写100个字节数据时, 可以调用一次write写100个字节, 也可以调用100次write, 每次写一个字节;
- 读100个字节数据时, 也完全不需要考虑写的时候是怎么写的, 既可以一次read 100个字节, 也可以一次read一个字节, 重复100次
提示:TCP协议不关心上层的协议内容和报文格式,TCP对于数据只有字节的概念。
十、粘包问题
粘包问题指的是发送方发送的数据包在传输过程中被合并成一个或多个更大的数据包,而接收方在接收到这些数据包时无法正确区分原始数据包的边界,从而导致数据解析出错或者数据丢失。由于TCP是基于字节流的协议,数据会像流水一样连续不断地传输,接收方无法直接判断数据的开始和结束,因此容易出现粘包现象。
10.1 粘包问题的原因
粘包问题的产生主要有以下几个原因:
- TCP的流式协议特性:TCP是一种面向连接的、可靠的、基于字节流的传输层通信协议。数据在传输过程中会被TCP协议自动拆分成多个TCP段进行传输,而接收方在接收到这些TCP段后,会将其合并成一个连续的数据流,这就可能导致多个数据包被合并成一个数据包。
- 缓冲区机制:TCP协议在传输数据时会使用缓冲区来暂存数据。如果发送方发送的数据量较小,且发送时间间隔较短,这些数据就可能会被合并成一个TCP段进行发送。同样,接收方在接收数据时也会先将数据存储在缓冲区中,待缓冲区满或接收到特定的结束标志后再进行处理。如果接收方没有及时处理缓冲区中的数据,就可能导致多个数据包被一起处理,从而产生粘包现象。
- Nagle算法:TCP为了提高传输效率,会采用Nagle算法对发送的数据进行合并处理。当发送方有多个小数据包需要发送时,Nagle算法会将这些小数据包合并成一个较大的数据包进行发送,以减少网络拥塞和提高传输效率。然而,这也可能导致接收方在接收到数据包时无法正确区分原始数据包的边界。
10.2 解决粘包问题的方法
针对粘包问题,可以采取以下几种解决方法:
- 采用定长报文:如果发送的数据包大小是固定的,那么接收方可以在接收到足够长度的数据后直接进行处理。这种方法适用于数据包大小固定且变化不大的场景。
- 特殊分隔符:在数据包的末尾添加一个特殊的分隔符(如换行符、回车符等),接收方在接收到数据时可以根据这个分隔符来判断数据包的结束。然而,这种方法需要确保特殊分隔符不会出现在数据包的正常内容中。
- 长度字段:在每个数据包的开头添加一个表示数据包长度的字段(通常是整数类型),接收方在接收到数据包时首先读取这个长度字段(通过定长报文或特殊字符分割),然后根据长度字段的值来读取相应的数据。这种方法可以有效地解决粘包问题,但需要确保长度字段的准确性和可靠性。
- 使用高级协议或框架:许多高级的网络通信协议或框架(如HTTP、WebSocket、Netty等)已经内置了解决粘包问题的机制。在使用这些协议或框架时,可以直接利用其提供的API来发送和接收数据,而无需担心粘包问题。
十一、TCP异常情况处理
TCP(传输控制协议)在应对异常情况时,具有一套完善的机制来确保数据传输的可靠性和稳定性。以下是TCP在几种常见异常情况下的应对策略:
进程崩溃
- 处理机制:当进程崩溃后,其进程控制块(PCB)也会被摧毁,包括其中的文件描述符表。文件描述符表中包含Socket网卡文件,释放Socket网卡文件相当于调用Socket.close(),从而触发FIN(结束报文段)请求断开连接,进一步触发四次挥手流程,确保连接正常释放。
- 结果:这与进程正常退出的处理没有区别,TCP连接通过四次挥手被正常释放。
主机关机(正常关机)
- 处理机制:正常关机时,系统会尝试摧毁所有进程(强制终止进程),这与进程崩溃的处理方式类似。在触发四次挥手断开网络连接的过程中,主机可能已经完成关机。
- 结果:即使四次挥手没有完成,每个进程都会发送FIN给网络连接的另一方,对方在多次重传未得到应答后会单方面断开连接。
主机掉电(拔电源)
- 处理机制:
- 如果发送方掉电,接收方通过心跳包机制在等待一段时间后未收到应答报文段,会触发超时重传。多次重传失败后,接收方会尝试重置连接,若重置失败则单方面断开连接。
- 如果接收方掉电,发送方在发送数据后未收到应答报文段,同样会触发超时重传和可能的重置操作。
- 心跳包机制(保活机制):心跳包机制(Heartbeat Mechanism)是网络通信中用于检查连接状态和保持连接活跃的机制。它通过定期发送小的“心跳”消息来确认连接是否仍然有效。TCP本身支持心跳包,但往往需要在应用层重新实现更频繁的心跳包(如秒级或毫秒级),以更快地发现服务器问题。
网线断开
- 处理机制:网线断开的情况类似于主机掉电,但更直接地中断了物理连接。发送方在发送数据后未收到应答报文段,会触发超时重传和可能的重置操作。
- 结果:与主机掉电的处理结果相似,发送方最终会单方面断开连接。
网络拥塞
- 拥塞控制:TCP通过慢启动、拥塞避免、超时重传和慢启动、快速重传和快速恢复等机制来应对网络拥塞。
- 结果:当发生网络拥塞时,网络中所有检测到拥塞情况的TCP/IP协议主机都会通过各种拥塞控制机制限制网络传输的速率,使得拥塞的数据在网络中尽快消散,恢复网络信道的通畅。
数据包丢失
- 处理机制:超时重传和快重传机制。当发送端连续收到三次失序的ACK报文,就会触发快重传机制,补发丢失的报文段。快重传不需要等待超时事件发生,可以实现快速补发。而如果出现网络拥塞或是一些特殊情况导致计时器超时,就会触发超时重传机制,超时重传是兜底的重传方案。
- 结果:有这两种重传机制的存在,TCP协议在面对任何数据包丢失的情况,都能够可靠且高效的实现补发。
十二、套接字和文件的关系
12.1 普通的磁盘文件
- f_op:指向的是磁盘文件的操作方法集
- f_mapping:文件的读写缓冲区
12.2 套接字文件
struct file::f_op
:指向的是网络的相关方法。主要是向上对接应用层的操作方法,如将应用层数据拷贝到传输层(内核)。struct file::private_data
:指向struct socket。从应用层通过文件描述符就可以找到该套接字的socket结构struct socket::type 和 flags
:表明套接字使用的传输层(tcp/udp)和网络层协议(ipv4/ipv6)struct socket::file
:回指向与之相关联的struct filestruct socket::wait
:当网络资源不就绪时将进程的task_struct链入到wait队列中阻塞等待struct socket::ops
:主要是网络协议对应的操作方法,如建立、关闭链接等链接管理方法。struct socket::sk
:指向struct sock。这里涉及C语言实现的继承和多态方案:struct sock有两个派生类tcp_sock和udp_sock,他们的首部(起始地址处)都是基类struct sock,通过这种结构体嵌套结构体的方式实现属性的继承。通过函数指针调用的方式实现方法的多态调用。struct sock::sk_receive_queue 和 sk_write_queue
:这就是我们常说的套接字的读写缓冲区了。对于TCP协议,读和写缓冲区都启用;而对于UDP协议,没有写缓冲区。读写缓冲区的类型sk_buff_head并不是我们一直认为的线性结构,而是以链表的形式组织sk_buff的链式结构。- 每一个TCP链接就对应一个套接字(文件描述符),对应一个struct file,对应一个struct socket,对应一对发送和接收缓冲区
struct sk_buff_head::lock
:在内核当中接收和发送数据的过程本质就是生产者消费者模型,因此sk_buff_head作为数据的缓冲区需要互斥锁进行保护。链表节点的增加和移除速度很快,等待时间短,因此使用自旋锁加锁。sk_buff
:sk_buff结构是linux网络代码中重要的数据结构,它负责管理和控制接收或发送数据包的信息。可认为sk_buff就是报文的实体。通过4个指针将数据分为:头空间,数据,和尾空间。封装本质上就是移动sk_buff的指针,将上层报文整体作为数据,在数据之前加入本层协议的报头,然后再将其传递给下层协议继续封装- 协议栈中的每一层都有特定的数据结构和方法集(通过函数指针的方式提供)。也就是说tcp/udp不会将数据直接发送到网络,而是将数据经过传输层的处理和封装后交给下一层网络层。网络层再使用该层的方法集对数据进行处理…
- 报文在协议栈上下层之间的传递,本质上就是sk_buff结构在层和层的缓冲区之间进行流动
- 操作系统底层存在大量的tcp报文,这些报文可能来自或去往不同的上层进程,操作系统维护着一个发送队列和一个接收队列对这些报文做统一的组织和管理。
- 报文的分用本质上就是通过端口号将系统接收队列中的sk_buff结构移动到对应进程的接收缓冲区中去。
总结:
- 体会建立连接是需要成本的
- 理清网络套接字和文件的关系
- 深入理解数据在协议栈中的传递
注意:以上是对Linux内核源码的摘要解析,实际上每层协议功能的实现要复杂的多,对应的数据结构也更为复杂:当中包含多层嵌套和类型重命名。因为没有学过网络层和链路层协议的原因,可以发现我们的理解在接近底层协议时发生了模糊。待日后将整个网络协议栈学完后,我们就彻底弄明白数据在协议栈之间的流动过程了。
十三、总结
13.1 可靠性和高性能机制
为什么TCP这么复杂? 因为要保证可靠性, 同时又尽可能的提高性能.
可靠性机制:
- 校验和:用于检测数据在传输过程中是否发生损坏或错误,保证数据的完整性。
- 序列号:用于实现数据的可靠传输和顺序重组,确保数据包的正确接收和按序传递。
- 确认应答:用于保证数据的可靠传输,发送方通过接收到的确认应答来确认数据已经被成功接收,以触发重传机制来应对丢包和错误,确保数据的完整性。
- 超时重传:用于检测数据包的丢失,通过在规定时间内未收到确认应答时重新发送数据包,确保数据的可靠传输。
- 连接管理:用于建立和维护双方的通信连接,确保数据的可靠传输。
- 流量控制:用于调整发送方的发送速率,确保发送和接收方之间的数据传输平衡,防止数据溢出和丢失,优化网络性能。
- 拥塞控制:用于监控网络拥塞情况,以动态调整发送速率,防止网络拥塞,优化网络性能。
高性能机制:
- 滑动窗口:用于动态管理数据的发送和接收,允许发送方在未收到确认的情况下连续发送多个数据包,从而提高整体传输效率。
- 快速重传:用于在接收到重复确认时迅速重传丢失的数据包,减少延迟并提高数据传输的效率和可靠性。
- 延迟应答:用于减少网络拥塞和提高效率,通过在接收到数据后延迟发送确认应答,让接收端在这段时间内能够释放一部分接收缓冲区的空间,从而允许发送端在后续的发送过程中拥有更大的窗口大小,进而提高网络的吞吐量和传输效率。
- 捎带应答:用于在发送数据包时,将确认应答一起发送,以减少网络往返延迟,从而提高数据传输效率。
13.2 基于TCP的应用层协议
- HTTP
- HTTPS
- SSH
- Telnet
- FTP
- SMTP
13.3 对比TCP和UDP
TCP(传输控制协议)和UDP(用户数据报协议)都是互联网协议套件中的一部分,它们都属于传输层协议,但它们在设计目标、工作机制和应用场景上存在显著差异。下面是TCP和UDP之间的一些主要对比:
- 可靠性
- TCP:是一种面向连接的、可靠的、基于字节流的传输层通信协议。TCP通过序列号、确认应答、超时重传、流量控制、拥塞控制等机制来确保数据的可靠传输。如果数据包在传输中丢失或出错,TCP会重新发送这些数据包,直到它们被正确接收。
- UDP:是一种无连接的、不可靠的协议。UDP不保证数据包的顺序、完整性或正确性。如果数据包在传输过程中丢失或出错,UDP不会重发这些数据包,也不会向发送方提供任何错误反馈。
- 面向连接 vs 无连接
- TCP:在传输数据之前,TCP协议会先建立连接,即进行三次握手,确保发送方和接收方之间的通信链路是可用的。数据传输完成后,还会进行四次挥手来关闭连接。
- UDP:不需要事先建立连接,每个数据包都是独立传输的。发送方直接发送数据包到网络上,而无需关心接收方是否准备好接收。
- 头部开销
- TCP:TCP头部较大,通常包含20字节的固定部分和一些可选字段(如时间戳、选项等),这些字段用于实现TCP的各种功能。
- UDP:UDP头部较小,只有8字节,包括源端口号、目的端口号、数据长度和校验和。这使得UDP在传输少量数据时比TCP更高效。
- 实时性
- TCP:由于TCP需要建立连接、进行错误检查和重传等操作,因此在实时性要求较高的场合(如视频直播、在线游戏等)可能不是最佳选择。
- UDP:由于UDP没有连接建立、错误检查和重传等开销,因此其传输延迟较低,更适合对实时性要求较高的应用。
- 应用场景
- TCP:适用于需要可靠传输的场合,如文件传输、网页浏览、电子邮件等。
- UDP:适用于对实时性要求较高、可以容忍一定数据丢失的场合,如实时视频传输、在线游戏等。
总结来说,TCP和UDP各有优缺点,选择哪种协议取决于具体的应用场景和需求。