趣谈网络协议笔记-二(第十二讲)

趣谈网络协议笔记-二(第十二讲)

TCP协议(下):西行必定多妖孽,恒心智慧消磨难


前言

哈哈哈,越当我看刘超的通俗讲解,我就越感觉自己的无能。每次当我看了讲解之后,每次当我感觉到这个东西原来是这么简单的东西时,都会情不由衷地感觉到自己,到底是有多么的软弱,多么的脆弱。因为既然是别人打算讲解给你听的东西,那么别人一定是做好充分准备,力求让自己所讲的东西就算是让一头猪来听,都能听得似是而非的。

那么既然是连猪都能听得懂的事情,我能听得懂到底有什么值得沾沾自喜的呢?所以,当参加任何的培训的时候,听别人侃侃而谈,当你感觉他所说的事情似乎很简单的时候,请注意,此时你应该反问自己,“如果他不讲,我知道这些吗?”如果并不是,那么并不是他讲的东西对于你来说太过于简单,仅仅是他比较擅长将复杂的,晦涩的事情讲的简单而已。

我也感觉到,如果今天我不为自己的脆弱而努力,那么明天,我绝对会为自己的脆弱而买单!

今天在公司同一层楼遇到了我的高一同学,为什么不是高中同学呢,因为高一分实验班的时候我入选了= =!大概算了算,8年了呢,茫茫13亿人,能再次相遇也算是一种缘分,找个时间,吃顿饭好好聊聊吧,这几年真的是发生了很多事情呢。

共勉!王国兴废,在此一战,诸君努力!

正文

上一讲主要讲的是TCP协议在客户端和服务器端之间是如何建立和停止可靠(相对UDP)的连接的。
这一讲则是用以讲述,在建立起可靠的连接之后,客户端和服务器是如何执行可靠的数据传输的。

这一讲不像上一讲一样,我能想到比较好的载体,所以晚上犹豫了一下,到底应该怎么进行描述和表达才能将自己所理解的事情很清晰地展现给读者,最近,也在进行学习,如何记录可视化笔记,我相信,等我学成,博客表达什么的将不再是问题。

那么客户端是如何通过TCP协议将数据可靠地发送到服务器的呢?我们不妨做一个比喻,将客户端比作老师,将服务器比作学生,将数据传输比喻成老师给学生布置作业

作为老师一方,升学率的压力其实逼迫着老师只能尽可能地让学生花费更多的时间来学习,其中最简单的方式当然是布置作业。但是,布置的作业数量也绝对不能太过分,毕竟学生的时间的精力有限,如果作业超出了学生的负担,一般就会导致两个结果,学生连作业都背不动“丢包”,学生到交作业的时候作业没有做完“服务器不处理包”。

所以这里就师生之间就需要进行一些协商了,在布置作业的时候,学生就会说,“老师,行行好吧,我今晚肾虚,最多只能做这么多的作业了”(在包头中声明窗口大小)。老师也很好说话,“那好吧,今天晚上我就布置这么多的作业,但是这些作业你一定要完成啊”(客户端就基于服务器传回来的窗口大小来控住发送包的频率,注意,是基于,而不是完全按照)。请注意,学生完成作业,老师是要检查的,但是一般很少会有老师会让学生每完成一页作业,就给老师打一个电话(即每收到一个id的包就返回一次应答),那全班那么多的学生,老师也忙不过来。所以老师一般一天检查一次作业,学生将前一天的作业的完成情况进行一次汇总,汇报给老师(服务器会应答某个之前的id,表示都收到了,称为“累计确认”)。

之前也说过了,老师在布置作业的时候,也需要考虑学生在路上抗不抗的动,比如说,”我给你一堆坦克零件,你回去给我组装好!“学生的负载能力也是需要考虑在内的(坐车啊,坐飞机啊)。

教师布置的作业多少毫无疑问要基于以上两点,负载能力(网络的传输效能cwnd)和学生执行力(服务器的处理效能rwnd),往往取其中的弱者,短板效应。公式如下
LastByteSent - LastByteAcked <= min{cwnd,rwnd}

首先我们先默认学生的能力是那块短板,即窗口的大小受服务器的缓存大小所影响。
[image:37570786-E2C0-4C2B-9A10-6F8D2EA84E97-301-00000A2C1F26435C/16dcd6fb8105a1caa75887b5ffa0bd7b.jpg]

客户端维护一张需要通过TCP协议进行传输的包的序号表,表中分为四个部分
* 发送已确认:老师已经布置,学生已经完成的作业页数
* 发送未确认:老师已经布置,但是学生还在做,不知道有没有完成
* 未发送可发送:老师感觉学生今天作业太少,打算在家长群里额外布置的作业
* 未发送不可发送:老师预期明天要布置的作业

与之相对的,服务器端也维护一张序号表,用以表示具体接收到包的序号数据信息,表的结构相对比较简单,分为三个部分:
* 接收已确认:老师已经布置,学生已经完成的作业
* 等待接收未确认:老师已经布置,我正在做的作业
* 不能接收:老师超过我的能力,布置的作业,我当天也没有办法完成
[image:F313B296-C15D-47A7-9B8F-0356423931CE-301-00000A82734F4845/f7b1d3bc6b6d8e55f0951e82294c8ba4.jpg]

之前所说的”累计确认“就是指当发现1-5的序号包都已经收到了,就会应答序号5的包,表示1-5序号的包我都已经接收成功了。

那么如果我有一个包丢了怎么办呢?我们一般会设置一个超时时间RTT,如果超过了超时时间,但是还没有接收到ack应答包,就重新发送一次,同时将超时时间设置为原来的两倍。如果遭遇到了两次超时,那就说明,网络情况很糟糕,不适合反复发送。

当然不仅仅是客户端对丢包会做出反应,服务器端如果遇到了类似5号包都到了,但是4号包还没有到,就会连续发送三次基于3的ack包,来表示需要重新传输4号包。

另外,也可以通过在TCP头传输一个名为SACK(selective acknowledge)的东西,来表示服务器端缺乏了特定的包,ACK3,SACK5,SACK6。

OK,还有拥塞控制的TCP解决方案没有处理。
客户端对于网络情况其实是缺乏客观性了解的,对于客户端来说,与服务器端之间的网络就像是一个黑盒。作为客户端只能通过试探来逐渐摸清楚中间网络的情况。
思路如下:先假定网络的传输能力的极限为sshresh=65535,cwnd=1
传输一次,发现没有丢包,那么将当前cwnd翻倍试试看,即cwnd=2
以此类推,最后会归到两种情况
1. cwnd超过sshresh,这个传输速度已经足够满足大多数需求了,当然如果有资源绝对不能浪费,所以如果传输过程中还没有丢包,那么cwnd+1
2. cwnd达到一定的值时发生了丢包的情况,这个时候,我们认为我们过于乐观地估计了网络的情况,所以就将sshresh设置为当前cwnd的一半,如果是客户端发现丢包了,那么cwnd从1开始重新努力;但是如果是服务器端发现丢包了,这个情况好一些,那么cwnd就会很乐观地按照情况1的逻辑继续探测中间的网络情况。
[image:1FE5D779-D521-4C66-BF99-F4D3F39A36A3-301-00000CA348EA9522/1910bc1a0048d4de7b2128eb0f5dbcd2.jpg]

当然,这样的探测有两个问题:
1. 丢包并不代表着通道满了,也可能是管子本来就漏水。例如公网上宽带不满也会丢包,这个时候就认为拥塞了,退缩了,其实是不对的。
2. TCP的拥塞控制要等到中间设备都填充满了,才发生丢包,从而降低速度,在这个时候已经晚了。其实TCP只需要填满管道就可以了,不应该接着填,直到连缓存也填满。

为了优化这两个问题,后来有了TCP BBR拥塞算法。它企图找到一个平衡点,就是通过不断地加快发送速度,将管道填满,但是不要填满中间设备的缓存,因为这样时延会增加,在这个平衡点可以很好的达到高带宽和低时延的平衡。
[image:A1EF9C1D-30ED-48C3-8C87-A192CFDB7AB9-301-00000BDB28532C77/a2b3a5df5eca52e302b75824e4bbbd4c.jpg]

猜你喜欢

转载自blog.csdn.net/qq_31433709/article/details/107851244