TCP/IP详解 卷1:协议 学习笔记 第二十章 TCP的成块数据流 港控/mmm° 2022-10-14 12:57 117阅读 0赞 在bsdi上运行自定义的sock程序作为服务器: ![在这里插入图片描述][2021052117202381.png] 其中-i和-s选项指示程序作为一个从网络上读取并丢弃数据的服务器运行,且服务器端口为7777,相应的客户程序为: ![在这里插入图片描述][20210521172133808.png] 该命令发送8个1024字节的数据,以下是这个过程的时间序列: ![在这里插入图片描述][watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3R1czAwMDAw_size_16_color_FFFFFF_t_70] 前三个报文段是连接建立过程,建立时的报文段显示了两端的MSS值,连接建立后,发送方连续发送了三个数据报文段,但报文段7中只确认了前两个数据报文段(其确认序号为2048),仅确认前两个报文段的原因为:当一个分组到达时,它首先被设备中断例程处理,然后放置到IP的输入队列中,三个报文段4、5、6依次到达并按接受顺序放到IP的输入队列,IP按同样顺序将它交给TCP,当TCP处理报文段4时,报文段4的确认需要等待以发送一个经受时延的确认,之后TCP处理报文段5,此时TCP有两个未完成的报文段需要确认,因此产生了报文段7,它只对两个报文确认,此时清除了该连接产生的经受时延的确认,之后TCP处理报文段6,该连接又被标志为产生一个经受时延的确认,在报文段9到来之前,时延定时器溢出,产生一个对第三个数据报文段及进行确认的报文段。 报文段11~16说明了通常使用的隔一个报文段确认的策略,报文段11、12、13到达并被放入IP的接收队列,当报文段11被处理时,连接被标记为一个产生一个经受时延的确认,当报文段12被处理时,它们的ACK产生且连接的经受时延的确认标志被清除,报文段13使得连接再次被标记为产生经受时延的确认,但在时延定时器溢出前,报文段15被处理完毕,因此在定时器溢出前报文段13和15的确认被发送。 上图中所有发送方发送的8个数据报文段全都设置了PUSH标志,这是因为每次写操作都清空了发送缓存。 接收方不必确认每个收到的分组,TCP的确认是累积的,它表示接收方已经正确接收到了一直到确认序号减1的所有字节。 线路上的分组依赖于许多无法控制的因素,发送方TCP的实现、接收方TCP的实现、接收进程读取数据(依赖于操作系统的调度)、网络的动态性(如以太网的冲突和退避等),没有一种单一的、正确的方法交换给定数量的数据,如果再次发送与上例相同的数据,分组交换情况也可能会不同。 以下是一个快的发送方到一个慢的接收方的时间序列: ![在这里插入图片描述][watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3R1czAwMDAw_size_16_color_FFFFFF_t_70 1] 发送方连续发送四个数据报文段去填充接收方的窗口,之后停下来等待ACK,接收方在报文段8中发回ACK,但通告其窗口大小为0,说明接收方已经接收到了所有数据,这些数据全部在接收方的TCP缓冲区,应用还没读取这些数据,另一个ACK(称为窗口更新)在17.4ms后发送,表明接收方现在可以接收另外的4096字节的数据,它虽然看起来像ACK,但不确认任何新数据,只是用来增加窗口的右边沿。 发送方之后在报文段10~13中再次发送了四个1024字节大小的数据,且报文段13带有PUSH和FIN标志,随后接收方发来两个ACK表示对这四个数据报的确认,之后接收方发出FIN。 上图中,前四个数据报文段(报文段4~7)每一个都设置了PUSH标志,这是因为它们每一个均使发送缓存为空了,随后,TCP停下来等待一个确认来移动移动窗口,在此期间,TCP又得到了应用的4096个字节,当窗口张开时,TCP知道它有4个可立即发送的报文段,因此它只设置了最后一个报文段的PUSH标志。 ![在这里插入图片描述][watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3R1czAwMDAw_size_16_color_FFFFFF_t_70 2] 接收方通告的窗口称为提供的窗口,它覆盖了第4~9字节的区域,表明接收方已经确认了之前的数据,当前的通告窗口大小为6,窗口大小是与确认序号相对应的,发送方计算它的可用窗口。 描述窗口左右边沿的运动的术语: 1.窗口左沿向右边靠近称为窗口合拢,发生在数据被发送后收到确认时。 2.窗口右沿向右移动时称为窗口张开,发生在另一端的接收进程读取已经确认的数据并释放了TCP的接收缓存时。 3.当右边沿向左移动时称为窗口收缩,Host requirements RFC强烈建议不要使用这种方式。 ![在这里插入图片描述][20210524173847537.png] 如果左边沿到达右边沿,称其为一个零窗口,发送方不能发送任何数据。 由接收方提供的窗口的大小通常可以由接收进程控制,这将影响TCP的性能。 socket API允许进程设置发送和接收缓存的大小,接收缓存的大小是该连接上能通告的最大窗口大小,一些应用通过修改发送和接收缓存大小来增加性能。 ![在这里插入图片描述][watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3R1czAwMDAw_size_16_color_FFFFFF_t_70 3] 由于接收方开始时提供的窗口大小为6144字节,客户发送了6个1024大小的报文段,然后停止,报文段10中确认了这6个报文段,但提供的窗口大小为2048,很可能是应用只读取了2048字节的数据。报文段11和12完成了客户的数据传输,且最后一个报文段带有FIN标志。 报文段13包含与报文段10相同的确认序号,但通告了一个更大的窗口大小,之后报文段14确认了最后2048字节的数据和FIN,报文段15和16仅用于通告一个更大的窗口大小。 上图中发送方的第12个报文段的PUSH被置位,因为它是最后一个报文段。第7个报文段也设置了PUSH标志,但发送方此时知道有更多的数据需要发送,原因在于接收方的窗口大小只有4096字节,第7个报文段会填满接收缓存。 当接收方通告其窗口大小为0(此时发送方将停止发送)后,当窗口再次张开时,需要发送另一个窗口非0的ACK来使发送方重新启动,但上图中,窗口的大小从来没有达到过0,然而,上图中当窗口大小增加了2048字节的时候,另一个ACK被发送以通知对方更新窗口(第15、16个报文段),这是由于许多TCP实现在窗口大小增加了两个最大报文段长度(上例中是2048字节,MSS是1024字节)或最大窗口的50%(上例中也是2048字节,最大窗口大小为4096字节)时,发送窗口更新。 发送方使用PUSH标志通知接收方将所受到的数据全部提交给接收进程,包括与PUSH一起传送的数据和接收方TCP已经为接收进程收到的其他数据。接收方接受到PUSH后立即将这些数据交给服务器进程而不等待判断是否还会有额外的数据到达。 最初的TCP规范中,一般假定编程接口允许发送进程告诉TCP何时设置PUSH标志,从而通知服务端TCP不要因等待额外数据而使已接收数据在缓存中滞留。 目前大多API没有向应用提供设置PUSH标志的方法,许多实现认为PUSH标志已经过时,一个好的TCP实现能自行决定何时设置这个标志。 如果待发送数据将清空发送缓存,则大多源于伯克利的实现能自动设置PUSH标志,这个算法对于只有在缓存被填满或收到一个PUSH标志时才向应用程序提交数据的TCP实现有效。 socket API不能通知TCP设置PUSH标志,也不能知道收到的数据是否设置了PUSH标志。 源于伯克利的实现一般从不将收到的数据推迟交付给应用,因此它忽略接收到的PUSH标志。 当发送方和接收方之间存在多个路由器和速率较慢的链路时,可能出现问题,慢启动算法缓慢探测可用传输资源,防止短时间内大量数据注入导致拥塞。 慢启动为发送方的TCP增加了拥塞窗口,记为cwnd,当与另一个网络的主机建立TCP连接时,拥塞窗口被初始化为1个报文段(即另一端通告的报文段大小),每收到一个ACK,拥塞窗口增加一个报文段(cwnd以字节为单位,慢启动以报文段大小为单位进行增加),这意味着当发送方收到第一个报文段的ACK时,会将拥塞窗口增加到2,之后发送两个报文段,当接收到这两个报文段的两个ACK时,拥塞窗口变为4,此时是指数增加的,而如果接收方将这两个ACK合并发送,则拥塞窗口变为3,发送方取拥塞窗口与通告窗口中的最小值作为发送上限,拥塞窗口是发送方的流量控制,而通告窗口是接收方使用的流量控制。 在某个点上达到了最大流量,中间路由器开始丢弃分组,这就通知发送方它的拥塞窗口开得过大。 ![在这里插入图片描述][watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3R1czAwMDAw_size_16_color_FFFFFF_t_70 4] 由上图,发送方在拥塞窗口为3的情况下,由于还没有收到报文段4的ACK,因此还是发送了2个报文段(6~7)。 窗口大小、窗口流量控制、慢启动对传输成块数据的TCP连接的吞吐量的相互作用: ![在这里插入图片描述][watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3R1czAwMDAw_size_16_color_FFFFFF_t_70 5] ![在这里插入图片描述][watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3R1czAwMDAw_size_16_color_FFFFFF_t_70 6] 在时间31之后,发送方和接收方之间的管道被填满,不能再容纳更多数据。从此每当接收方从网络上移去一个报文段,发送方就再发送一个报文段到网络上,返回路径上总是具有相同数目的ACK,这是连接的理想稳定状态。 以上通道的容量称为时延带宽积,计算公式如下: ![在这里插入图片描述][20210526170209234.png] 容量为带宽×RTT。 ![在这里插入图片描述][watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3R1czAwMDAw_size_16_color_FFFFFF_t_70 7] 当数据到达一个大的管道(如一个快速局域网),并向一个较小的管道(如一个较慢的广域网)发送时会发生拥塞。当多个输入流到达一个路由器,而路由器的输出流小于这些输入流的总和时也会发生拥塞。 以下是典型的拥塞,之所以说典型,是由于大多数主机都连接在一个局域网上,并通过一个路由器与速率相对较低的广域网相连: ![在这里插入图片描述][watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3R1czAwMDAw_size_16_color_FFFFFF_t_70 8] 通常,R1和R3是同一路由器,R2和R4也是,但这并不必须,有时也会使用非对称路径。上图假定发送方不使用慢启动,它按照局域网的带宽尽可能快地发送报文段。 TCP提供了紧急模式,来告诉接收端有紧急数据放置在了数据流中,接收方决定如何进行处理。 紧急模式打开方法是置位TCP首部中的URG比特,将一个16bit的紧急指针置为一个正的偏移量,该偏移量与TCP首部中的序号相加可得出紧急数据的最后一个字节序号。 紧急指针应指向紧急数据的最后一个字节还是该字节之后的一个字节还有争论,Host Requirements RFC确认指向最后一个字节是正确的,但大多数实现,包括伯克利的实现继续使用另一种错误方式。 TCP必须通知进程接收到了紧急数据,接着进程可读取数据流,并必须被告知何时碰到了紧急数据指针,只要接收方当前读取位置到紧急数据指针之间有数据存在,就认为应用处于紧急模式,紧急指针通过后,应用回到正常模式。 TCP不能指出紧急数据从数据流的何处开始。 许多实现不正确地称TCP的紧急模式为带外数据,如果一个应用确实需要一个独立的带外信道,第二个TCP连接是达到这个目的的最简单方法。许多运输层确实提供使用同一个连接的另一个逻辑数据通道作为正常的数据通道,但TCP没有提供此功能。 TCP的紧急模式与带外数据之间的混淆,是因为主要的编程接口socket API将TCP的紧急方式称为带外数据。 紧急方式可用于telnet和rlogin,当交互用户输入中断键时向服务器发送;也可用于FTP,当交互用户放弃一个文件的传输时。 有时客户会发送一个报文段以通告它的窗口为0,此时服务器TCP立即发送紧急指针和URG标志(即使服务器进程处于紧急方式,它不能发送任何数据,但服务器TCP会立即发送紧急指针和URG),当客户TCP收到这个通知时就会通知客户进程,于是客户可以从服务器读取其输入并打开窗口。 在接收方处理第一个紧急指针前,发送方多次发送带URG的报文时,接收方只会维护一个紧急指针,每当有新的紧急指针到达时覆盖它。 测试在接收方窗口关闭时,发送方发URG,使用sock程序实现,服务端启动时选项: ![在这里插入图片描述][20210527114723789.png] 服务端使用-P选项使得在建立连接后从网络读取前暂停10秒钟。 之后启动sock的客户端: ![在这里插入图片描述][watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3R1czAwMDAw_size_16_color_FFFFFF_t_70 9] 客户端使用-S选项指定一个8192字节大小的发送缓存,并使用-n选项向服务端发送6个1024字节的报文段,还是用-U选项使客户在发送第5个报文段前先发一个1字节数据的URG报文段。 ![在这里插入图片描述][watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3R1czAwMDAw_size_16_color_FFFFFF_t_70 10] 发送端连发四个报文段(报文段1~3、5)填满了接收方的接收缓存,之后发送端写了1个字节并设置URG发送,紧急指针被设为4098,尽管发送方不能发送任何数据,但还是发出了报文段6。发送端连续发送了5个这样的URG报文段,第一个URG报文段是客户端的-U选项带来的,之后两个URG报文段是进程写最后两个1024字节的数据时发送(尽管TCP不能发送这2048字节的数据,但每次应用执行写操作时,TCP的输出功能被调用,可见,当TCP发出URG报文段后,还会再发其他的URG报文段),第四个URG报文段是应用关闭其TCP连接时发出的(此时应用已经将所有数据都放到了发送缓存中)。第五个URG很可能是接收到第四行的ACK时产生的。 之后,接收方确认第四个报文段的1024字节的数据(报文段11),但同时通告窗口为0,发送方用一个包含紧急通知的报文段进行响应(报文段12)。 之后第十三行,接收方应用在10秒后被唤醒,并接收了2048字节的数据,于是接收方通告窗口为2048字节,于是发送方在报文段14、15中发送了最后的2048字节,其中,由于紧急指针在报文段14的范围内,因此它还被设置了紧急通知标志,而报文段15中则关闭了该标志。接收方在报文段16中再次通告了窗口,因此发送方就把最后的1个字节发送(报文段17),并且还设置了FIN标志,表示断开连接。 上图中,进入紧急模式时发送的字节序号是4097,但紧急指针指向4098,该实现将紧急指针设为紧急数据最后字节的下一个字节。 SYN和FIN在序号空间里各占一个字节,而其他TCP首部中的标志不占用序号。 如果API提供一种方法,使得发送方可以打开PUSH标志,而接收方可以查询一个接收的报文段是否设置了PUSH标志,此时该标志也不能被用作一个记录标记,因为当TCP超时后,会进行数据的重新分组。 [2021052117202381.png]: /images/20221014/1b38fcfa4b454f31943287f4e036c000.png [20210521172133808.png]: /images/20221014/ff839fbf3d354b388931518251097b62.png [watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3R1czAwMDAw_size_16_color_FFFFFF_t_70]: /images/20221014/ebb97fa94cb2448d9c857770a4f7c9fc.png [watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3R1czAwMDAw_size_16_color_FFFFFF_t_70 1]: /images/20221014/df4bbe10475240b585b3cd12a0245d15.png [watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3R1czAwMDAw_size_16_color_FFFFFF_t_70 2]: /images/20221014/a1aa800f475146e481ce6220a2458894.png [20210524173847537.png]: /images/20221014/b132dc28a06742e8a68ab76622f5b3c0.png [watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3R1czAwMDAw_size_16_color_FFFFFF_t_70 3]: /images/20221014/368685169743429a89dcb9658b40d6a4.png [watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3R1czAwMDAw_size_16_color_FFFFFF_t_70 4]: /images/20221014/e226a5fd24ea40179fae451629b05abc.png [watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3R1czAwMDAw_size_16_color_FFFFFF_t_70 5]: /images/20221014/8a04ec31cbfb4699825632a4471989f1.png [watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3R1czAwMDAw_size_16_color_FFFFFF_t_70 6]: /images/20221014/5558d28ba4f54e41acc7df3ad2b02985.png [20210526170209234.png]: /images/20221014/6b53ab6df9ee4f27868a70c9b3fbe49e.png [watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3R1czAwMDAw_size_16_color_FFFFFF_t_70 7]: /images/20221014/27c07ba96aac4868bd91ac2191758e80.png [watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3R1czAwMDAw_size_16_color_FFFFFF_t_70 8]: /images/20221014/3f3362c038d8436a8e4033bc2b35f3f4.png [20210527114723789.png]: /images/20221014/48947cea6765479fbc78a89248794515.png [watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3R1czAwMDAw_size_16_color_FFFFFF_t_70 9]: /images/20221014/3528a42ba8e048289ccda602feef9e11.png [watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3R1czAwMDAw_size_16_color_FFFFFF_t_70 10]: /images/20221014/6fd5d34bdc0a4db3abf615e94823a81e.png
还没有评论,来说两句吧...