TCP协议学习总结(中)
很多人都说TCP协议是一个十分复杂的协议,在学习当中,我对协议每一个问题都分解学习后,每一个分解我都能体会和理解它的要点,并不难理解。但我把这些拆分的细节合并后,确认感觉这样一个协议相对“臃肿”但又好像不得不这样做的感觉。也写过那么多年代码,我也十分理解这种“分布”和“一致”的协调,就好像CAP理论一样,更关键的是许多的CAP选择都是依赖于TCP这样可靠的协议之上,可想而知它“可靠性”的重中之中,我也看到了根基扎实稳重的重要性。当然技术还在不断进步,协议的完善和优化从没有停止,无论如何,学习还得继续。
TCP的交互数据流
1、交互式输入
有些场景比如聊天,对信息交互的时效性非常敏感,又还比如远程操作,我们在本地点击一下鼠标的远程操作,需要被操作设备非常及时的响应,这样一些场景就是交互式输入。
交互式输入是场景需要之一,它是牺牲了网络利用率而满足了时间的一种选择,试想一下如果客户端每一个小小的动作(如点击ENTER键产生1个字节)就要带着20个字节的TCP头部以及20个自己的IP头部,一共41个字节在网络上跑,如果每种交互都是这样的话,网络的利用率大大降低,所以这种情况更多适用于局域网内。而且在这种交互式交互时还可以看到TCP头部的PSH标识被设置了,它的意思是赶紧把数据交给应用程序,而不是先放缓冲区。
客户端的实时推送请求以及服务端的及时响应确实能保证了“实时交互”的效果,但这里会隐藏了一个客户端对服务端数据回显确认报文(ACK)的一个延迟发送。我们都知道TCP交互是一个可靠的协议,所以对数据的接收和确认是必然的要做的事情,我觉得既然客户端已经得到了服务端的及时响应,对服务端数据的确认响应已经不需要那么及时了。好好地在这一个点上的优化可以节省了网络的不必要浪费。
通常TCP在接收到数据时并不会立刻发送ACK,相反,它推迟发送,以便将ACK与需要沿该方向发送的数据一起发送(有时这种现象为数据捎带ACK)。绝大多数实现采用的时延为200ms,也就是说,TCP将以最大200ms的时延等待是否有数据一起发送,这样做就节省了不必要的网络开销,从这一点看,TCP协议也是想绝了,毕竟资源珍贵,容不得半丁点的浪费。
2、Nagle算法
虽然交互式输入更多适用在局域网,但广域网就不能用这是不可能的,实时交互式这种场景会产生许多的(微)小分组,例如41字节中的真正数据才1字节。这确实会增加广域网拥塞的可能性。但是“道高一尺,魔高一丈”,一种简单和好的方法应运而生,那就是Nagle算法。该算法要求一个TCP连接上最多只有一个未被确认的未完成的小分组,在该分组的确认到达之前不能发送其它的小分组,相反,TCP收集这些少量的分组,并在确认到来时以一个分组的方式发出去。该算法的优越之处在于它是自适应的:确认到达得越快,数据也就发送得越快。而在希望减少微小分组数目的低速广域网上则会发送更少的分组。
从上图可以看到,因为Nagle算法的生效,所以在“按键”输出后没有等到服务端响应确认前的后续4次数据输入都无法发送,而是等到服务端确认ACK到达后一起打包发送。有一点需要注意的是,这里的输入假设都是小分组输入,并没有很大的数据输入(不超过MSS)。Nagle算法算是在广域网上做了一个这种的选择,对数据交互实时性的影响不会太大,而又对互联网做了一层较好的保护,让交互的效果自适应网络的状态。
TCP的成块数据流
1、滑动窗口
交互式输入我觉得从综合场景来看,相对少数。大部分情况下,大块的数据流交互才是“王道”,这里并不是说我们平时的应用交互就不需要实时,只不过这个“实时”是相对的,在数据量大的情况下如何才是最佳的交互体验,具体问题具体分析才是王道。在TCP头部中我们知道有一个叫做“16位窗口大小”的属性,这个窗口在上一节也介绍过相当于数据接收的“缓存区”,数据交互的双方(无论主动还是被动)都会维护着自己的一个窗口,应用程序没有消费数据之前都是停留在这个缓冲区中。所以,窗口可以看做TCP交互限流的一个关键所在,如果窗口爆满的一方是无法接收数据了,发送方也只能暂停发送,等待对方窗口的空闲才继续发送。毕竟双方应用程序处理数据的速度不一或者网络网速的客观影响,很难确认完美的状态,所以很需要一个像窗口一样的概念去维护双方之间的一个传送速度。
通过上图可以看到,这次大块数据交互不像“交互式输入”那样小分组发送,而是每次发送都会充满整个报文段允许的最大值(MSS)从而达到更加的效率,当然这已经是另一次场景了,而这个场景才是我们平常使用到的场景,例如你看个新闻,刷个微博等。从图中也可以看得到窗口的真实作用,服务端一开始就告诉客户端它的窗口大小为4096,客户端要发送的数据远远大于4096(例如一张图片可以就不止了),客户端可以连续以最大报文段的容量连续发送,直到服务端的窗口(缓冲区)被沾满时,才会停止发送,等待服务端的窗口大小变更通知,再继续发送。其实这里的成块数据传送隐藏了许多的规则细节,例如发送的数据不够MSS怎么办,需要等么?又或者服务端的窗口可以腾出了1个字节的空间也要告诉客户端的话就会引发“糊涂窗口综合症”的问题。这些细节后续会慢慢介绍,这里更多先总结窗口的基本作用。
窗口(缓冲区)的总大小是固定的,只不过因为窗口的“空闲”是随着接收方的确认而变动,从而让窗口看起来是动态变化的,这个动态(左右收缩)其实是站在双方的视觉看窗口的空闲状态而言,双方都会计算和维护当前窗口的大小, 例如发送方会计算它到底还有多少数据可以发送,而接收方会计算它到底还有多少窗口空间可以接收数据:
1)、称窗口左边沿向右边沿靠近为窗口靠拢,这种现象发生在数据被发送和确实时;
2)、当窗口右边沿向右移动时将允许发送更多的数据,我们称之为窗口张开。这种现象发生在另一端的接收进程读取已经确认的数据并释放了TCP的接收缓存时;
3)、当右边沿向左移动时,我们称之为窗口收缩。RPC强烈建议不要使用这种方式。
如果左边沿到达右边沿,则称其为一个零窗口,此时发送方不能够发送任何数据。就像“TCP窗口样例”显示的那样。不同系统默认的窗口大小不一样,例如有些是2048字节,有些是4096、8192或更大。但重要是“插口API允许进程设置发送和接收缓存的大小,接收缓存的大小是该连接上所能通告的最大窗口大小,有一些应用程序通过修改插口缓存大小来增加性能”。
2、慢启动
“接收窗口”并非万能,虽然可以通过增加缓存大小提高性能,但影响性能的远不止“接收窗口”的大小,还有网速、路由器等。迄今为止,发送方一开始便向网络发送多个报文段,直至达到接收方通告窗口大小为止。当发送方和接收方处于同一个局域网时这种方式是可以的,但是如果在发送方和接收方之间存在多个路由器时,就有可能出现一些问题。一些中间路由器必须缓存分组,并有可能耗尽存储器的空间。
现在TCP需要支持一种被称为“慢启动(slow start)”的算法。该算法通过观察到新分组进入网络的速率应该与另一端返回确认的速率相同而进行工作。慢启动为发送方的TCP增加了另一个窗口叫“拥塞窗口(congestion window)”,记为cwnd。拥塞窗口是发送方使用的流量控制,而通告窗口则是接收方使用的流量控制。
拥塞窗口被初始化为1个报文段(即另一端通告的报文段大小),收到一个ACK后,拥塞窗口从1个报文段增加为2,即可发送两个报文段,当继续收到这两个报文段的ACK时,拥塞窗口就增加为4,这是一种指数增加关系。发送方去拥塞窗口与通告窗口中最小值作为发送上限。其实这宗一种渐进循环的策略,当吞吐量在某些点上达到了互联网的容量时,于是中间路由器开始丢弃分组,而通知发送方它的拥塞窗口开得过大了。这里又隐藏了许多的实现细节,如丢失重传问题以及如何避免一下子又从慢启动起步这种慢效率的传输过程。这些后续会学习到。这里更多先对慢启动算法的一个初步认识。
窗口的大小应该设置为多大呢?也就是我们的TCP缓存应该设置多大呢?按理论计算,这个应该跟“带宽时延乘积”有关,具体公式如下:
capacity(bit)=bandwidth(b/s)* round-trip time(s)
这个值依赖于网络速度和两端的RTT,可以有很大的变动。例如一条穿越美国(RTT约为60ms)的T1的电话线路(1544000b/s)的带宽延迟乘积为11580字节。这是没有问题的,但对于一条穿越美国的T3电话线路(45000000b/s)的带宽时延乘积则为337500字节,这个值远远超过了最大所允许的TCP通告窗口大小(16字节窗口大小=65535字节)。别忘记了TCP头部还有一各选项可以补充,后续会介绍如果通过选项解决这种超大型窗口问题。“带宽时延乘积”是一个理论值,就是我们能往发送到到接收端之间的网络最多塞满多少数据。
学习总结
本此总结主要是学习了基于TCP协议交互的一些流程与细节问题,如“交互式输入”以及“成块数交互”的场景介绍,在“成块式传送”中是如何利用“通告窗口”作为接收方的限流以及发送方如何利用“拥塞窗口”进行限流并借助“慢启动”的方式渐进循环的摸索互联网的最大极限。
TCP协议学习总结(中)的更多相关文章
- TCP协议学习总结(上)
在计算机领域,数据的本质无非0和1,创造0和1的固然伟大,但真正百花齐放的还是基于0和1之上的各种层次之间的组合(数据结构)所带给我们人类各种各样的可能性.例如TCP协议,我们的生活无不无时无刻的站在 ...
- 计算机网络:这是一份全面 & 详细 的TCP协议学习指南
原文链接:blog.csdn.net 用这个媒体播放器组件,实时互动时也可共同观看本地视频juejin.im 前言 计算机网络基础 该是程序猿需掌握的知识,但往往会被忽略 今天,我将详细讲解计算机网络 ...
- TCP协议学习
一.TCP参考模型 VS OSI参考模型 二.TCP/IP分层模型的四个协议层分别完成以下的功能 第一层 网络接口层 网络接口层包括用于协作IP数据在已有网络介质上传输的协议.实际上TCP/IP标准 ...
- TCP协议学习笔记(一)首部以及TCP的三次握手连接四次挥手断开
TCP协议是一种面向连接的.可靠的流协议. 流即不间断的数据结构.这样能够保证接收到数据顺序与发送相同.但是犹如数据间没有间隔,因此在TCP通信中,发送端应用可以在自己所要发送的消息中设置一个标示长度 ...
- TCP协议学习总结(下)
在前两边TCP学习总结中,也大概地学习了TCP的整个流程,但许多细节中的细节并没有详细学习,例如超时重传问题,每次瓶颈回归慢启动效率问题以及最大窗口限制问题等.本学习篇章最要针对这些细节中的细节进行学 ...
- TCP协议学习笔记
TCP协议数据格式 TCP协议在互联网ISO协议的传输层. 在互联网传输过程中,互联网包在数据链路层,是传输数据的最基础的包.一个互联网的包包含IP包,即互联网包 = 互联网信息包头(至少20字节)+ ...
- TCP协议学习总结
1.TCP协议通过三次握手建连接,四次挥手断连接. 2.TCP的定时器都有哪些? 做什么用途? 3.TCP的慢启动是什么意思? 4.TCP的快速重传是什么意思?
- TCP协议学习记录 (三) Ping程序 RR选项 记录路由hop
一开始想直接在上个程序改,自己构造IP包头,但后来发现不行,微软不让干了,所以后来选用libcap库来收发包 代码写的很乱.. #pragma pack(4) #define ECHO_REQUEST ...
- TCP协议学习记录 (二) Ping程序
简单的实现了一个ping程序,没有做icmp差错报文的检查. 支持自定义字节数,支持发包个数 #pragma pack(4) #define ECHO_REQUEST 8 #define DATASI ...
随机推荐
- IDEA创建完整目录maven项目
鉴于第三次利用idea建立完整目录maven项目时仍要反反复复地问度娘,特此记录 1.第一步,进行project-> 勾选Create from archetype,搜索maven.archet ...
- bzoj 2599
还是点对之间的问题,果断上点分治 同样,把一条路径拆分成经过根节点的两条路径,对不经过根节点的路径递归处理 然后,我们逐个枚举根节点的子树,计算出子树中某一点到根节点的距离,然后在之前已经处理过的点中 ...
- memset函数的实现&printf函数几种输出格式的输出结果
#include<stdio.h> #include<stdlib.h> void *memmset(void *dest, int ch, int count){ void ...
- layui报错 "Layui hint: 模块名 xxx 已被占用" 的问题解决方案
由于扩展模块数量众多, 于是我需要将扩展模块分类到二级文件夹中, 我在页面中是这么写的 <script> layui.extend({ courseTask: 'task/courseTa ...
- 手把手教你从ESXI部署到vSphere web Client管理控制
作为实验环境,一台物理机即可 既然是实验环境,那么首先把这个物理机装成ESXI6.5的宿主机并配置网络系统 第二步骤就是在ESXI上面导入OVF文件,注册一台虚机,作为数据管理中心 第三步骤就是基于这 ...
- python3_猜数字
import random count = 0while count<3: count +=1 number = int(input("猜数字:").strip()) num ...
- Python 判断文件后缀
方法1, str的endswith方法: ims_path='data/market1501/Market-1501-v15.09.15/bounding_box_test/12312.jpg' im ...
- idea+scala sdk + scala插件
0X01 前言 我的主语言是python,说起java,想起了大二(三年前)上课时教过,课程设计的时候曾经做过个俄罗斯方块,后面其他设计copy代码读懂代码(再后面的课设就用python了). 本次涉 ...
- tp5.0.7 修复getshell漏洞
这里 接手项目用的是 tp5.0.7 突然想到前段事件的tp bug 事件 就试了下 发现确实有这种情况 参考帖子: https://bbs.ichunqiu.com/thread-48687-1-1 ...
- idea中自动生成实体类
找到生成实体的路径,找到Database数据表 找到指定的路径即可自动生成entity实体 在创建好的实体类内如此修改 之后的步骤都在脑子里 写给自己看的东西 哪里不会就记录哪里 test类(以前都 ...