乐趣区

关于网络编程:网络编程入门从未如此简单二假如你来设计TCP协议会怎么做

本文原题“你管这破玩意儿叫 TCP?”,由闪客 sun 分享,转载请分割作者。

1、引言

网络编程能力对于即时通讯技术开发者来说是基本功,而计算机网络又是网络编程的实践根基,因此粗浅精确地了解计算机网络常识显然能夯实你的即时通讯利用的实际品质。
本文格调相似于《网络编程懒人入门》、《脑残式网络编程入门》两个系列,但艰深又不失外延,简洁又不简陋,非常适合对计算机网络常识有向往但又有害怕的网络编程爱好者们浏览,心愿能给你带来不一样的网络常识入门视角。
本篇将使用通俗易懂的语言,配上粗疏准确的图片动画,循序渐进地疏导你了解 TCP 协定的次要个性和技术原理,让 TCP 协定的学习不再如此干燥和生涩,非常适合入门者浏览。

本文已同步公布于“即时通讯技术圈”公众号,欢送关注。公众号上的链接是:点此进入。

2、系列文章

本文是该系列文章中的第 2 篇:

  • 《网络编程入门从未如此简略 (一):如果你来设计网络,会怎么做?》
  • 《网络编程入门从未如此简略 (二):如果你来设计 TCP 协定,会怎么做?》(本文)

本文次要波及计算机网络的传输层,心愿让 TCP 协定的学习不再干燥和生涩。

3、初识传输层

你是一台电脑,你的名字叫 A。

通过上篇《如果你来设计网络,会怎么做?》的一番折腾,只有你晓得另一位搭档 B 的 IP 地址,且你们之间的网络是通的,无论多远,你都能够将一个数据包发送给你的搭档 B。

上篇中分享的这就是物理层、数据链路层、网络层这三层所做的事件。
站在第四层的你,就能够不要脸地利用下三层所做的铺垫,得心应手地发送数据,而不用放心找不到对方了。

尽管你此时还什么都没干,但你还是给本人这一层起了个嘹亮的名字,叫做传输层。
你本认为本人所在的第四层高枕无忧,啥事没有,但很快问题就接踵而至。

4、问题来了

前三层协定只能把数据包从一个主机搬到另外一台主机,然而到了目的地当前,数据包具体交给哪个程序(过程)呢?

所以:你须要把通信的过程辨别开来,于是就给每个过程调配一个数字编号,你给它起了一个嘹亮的名字:端口号。

而后:你在要发送的数据包上,减少了传输层的头部:源端口号与指标端口号。

OK,这样你将本来主机到主机的通信,降级为了过程和过程之间的通信。
你没有意识到,你人不知; 鬼不觉实现了 UDP 协定!

当然 UDP 协定中不光有源端口和指标端口,还有数据包长度和校验值,咱们暂且略过。

就这样,你用 UDP 协定无牵无挂地同 B 进行着通信,始终没产生什么问题。

但很快,你发现事件变得非常复杂 … …

5、丢包问题

因为网络的不牢靠,数据包可能在半路失落,而 A 和 B 却无奈觉察。

对于丢包问题,只有解决两个事就好了。
第一个:A 怎么晓得包丢了?
答案是:让 B 通知 A。
第二个:丢了的包怎么办?
答案是:重传。
于是你设计了如下计划:A 每发一个包,都必须收到来自 B 的确认(ACK),再发下一个,否则在肯定工夫内没有收到确认,就重传这个包。

你管它叫进行期待协定。
只有依照这个协定来,尽管 A 无奈保障 B 肯定能收到包,但 A 可能确认 B 是否收到了包,收不到就重试,尽最大致力让这个通信过程变得牢靠,于是你们当初的通信过程又有了一个新的特色,牢靠交付。

6、效率问题

进行期待尽管能解决问题,然而效率太低了。
A 本来能够在发完第一个数据包之后立即开始发第二个数据包,但因为进行期待协定,A 必须等数据包达到了 B,且 B 的 ACK 包又回到了 A,才能够持续发第二个数据包。这效率慢得可不是一点两点。
于是:你对这个过程进行了改良,采纳流水线的形式,不再傻傻地等。

7、程序问题

然而网路是简单的、不牢靠的。
这导致的问题是:有的时候 A 收回去的数据包,别离走了不同的路由达到 B,可能无奈保障和发送数据包时一样的程序。

对应于咱们的例子:在流水线中有多个数据包和 ACK 包在乱序流动,他们之间对应关系就乱掉了。
如果回到下面的进行期待协定,那么 A 每收到一个包的确认(ACK)再发下一个包,那就基本不存在程序问题。但,应该有更好的方法吧?
是的,更好的方法就是:A 在发送的数据包中减少一个_序号_(seq),同时 B 要在 ACK 包上减少一个_确认号_(ack)。这样岂但解决了进行期待协定的效率问题,也通过这样标序号的形式解决了程序问题。

而 B 这个确认号意味深长:比方 B 发了一个确认号为 ack = 3,它不仅仅示意 A 发送的序号为 2 的包收到了,还示意 2 之前的数据包都收到了。这种形式叫_累计确认_或_累计应答_。

留神:实际上 ack 的号是收到的最初一个数据包的序号 seq + 1,也就是通知对方下一个应该发的序号是多少。但图中为了便于了解,ack 就示意收到的那个序号,不用纠结。

8、流量问题

有的时候,A 发送数据包的速度太快,而 B 的接管能力不够,但 B 却没有告知 A 这个状况。

怎么解决呢?
很简略:B 通知 A 本人的接管能力,A 依据 B 的接管能力,相应管制本人的发送速率就好了。
B 怎么通知 A 呢?B 跟 A 说 ” 我很强 ” 这三个字么?那必定不行,得有一个谨严的标准。
于是 B 决定:每次发送数据包给 A 时,顺带传过来一个值,叫_窗口大小_(win),这个值就示意 B 的_接管能力_。
同理:每次 A 给 B 发包时也带上本人的窗口大小,示意 A 的接管能力。

B 通知了 A 本人的窗口大小值,A 怎么利用它去做 A 这边发包的流量管制呢?
很简略:如果 B 给 A 传过来的窗口大小 win = 5,那 A 依据这个值,把本人要发送的数据分成这么几类。

图片过于清晰,就不再文字解释了。
当 A 一直发送数据包时,_已发送的最初一个序号_就往右挪动,直到碰到了窗口的上边界,此时 A 就无奈持续发包,达到了流量管制。

然而:当 A 一直发包的同时,A 也会收到来自 B 的确认包,此时_整个窗口_会往右挪动,因而上边界也往右挪动,A 就能发更多的数据包了。

以上都是在窗口大小不变的状况下。而 B 在发给 A 的 ACK 包中,每一个都能够_从新设置_一个新的窗口大小,如果 A 收到了一个新的窗口大小值,A 会随之调整。
如果 A 收到了比原窗口值更大的窗口大小,比方 win = 6,则 A 会间接将窗口上边界向右挪动 1 个单位。

如果 A 收到了比原窗口值小的窗口大小,比方 win = 4,则 A 临时不会扭转窗口大小,更不会将窗口上边界向左挪动,而是等着 ACK 的到来,一直将左边界向右挪动,直到窗口大小值膨胀到新大小为止。

OK,终于将流量管制问题解决得差不多了,你看着下面一个个小动图,给这个窗口起了一个更活泼的名字:滑动窗口。

9、拥塞问题

但有的时候,不是 B 的承受能力不够,而是网络不太好,造成了网络拥塞。

拥塞管制与流量管制有些像,但流量管制是受 B 的接管能力影响,而拥塞管制是受网络环境的影响。
拥塞管制的解决办法仍然是通过设置肯定的窗口大小。只不过,流量管制的窗口大小是 B 间接通知 A 的,而拥塞管制的窗口大小按理说就应该是网络环境被动通知 A。
但网络环境怎么可能被动通知 A 呢?只能 A 单方面通过试探,一直感知网络环境的好坏,进而确定本人的拥塞窗口的大小。

拥塞窗口大小的计算有很多简单的算法,就不在本文中开展了(有趣味能够深刻浏览《[[通俗易懂] 深刻了解 TCP 协定(下):RTT、滑动窗口、拥塞解决 ](http://www.52im.net/thread-51…》)。
如果拥塞窗口的大小为  cwnd,上一部分流量管制的滑动窗口的大小为 rwnd,那么窗口的右边界受这两个值独特的影响,须要取它俩的最小值。

窗口大小 = min(cwnd, rwnd)

含意很容易了解:当 B 的承受能力比拟差时,即便网络十分通顺,A 也须要依据 B 的接管能力限度本人的发送窗口。当网络环境比拟差时,即便 B 有很强的接管能力,A 也要依据网络的拥塞状况来限度本人的发送窗口。正所谓受其短板的影响嘛~

10、连贯问题

有的时候,B 主机的相应过程还没有筹备好或是挂掉了,A 就开始发送数据包,导致了节约。

这个问题在于:A 在跟 B 通信之前,没有当时确认 B 是否曾经筹备好,就开始发了一连串的信息。就好比你和另一个人打电话,你还没有 ” 喂 ” 一下确认对方有没有在听,你就巴拉巴拉说了一堆。
这个问题该怎么解决呢?
地球人都晓得:三次握手嘛!

  • A:我筹备好了 (SYN)
  • B:我晓得了 (ACK),我也筹备好了 (SYN)
  • A:我晓得了 (ACK)


A 与 B 各自在内存中保护着本人的状态变量,三次握手之后,单方的状态都变成了_连贯已建设_(ESTABLISHED)。
尽管就只是发了三次数据包,并且在各自的内存中保护了状态变量,但这么说总感觉太 low,你看这个过程相当于单方建设连贯的过程,于是你眉头一皱; 计上心来,就叫它面向连贯吧。

留神:这个连贯是虚构的,是由 A 和 B 这两个终端独特保护的,在网络中的设施基本就不晓得连贯这回事儿!

但凡事有始就有终,有了建设连贯的过程,就要思考开释连贯的过程。
这就是网络编程中耳熟能详的四次挥手啦!

  • A:再见,我要敞开了 (FIN)
  • B:我晓得了 (ACK)。给 B 一段时间把本人的事件解决完 …
  • B:再见,我要敞开了 (FIN)
  • A:我晓得了 (ACK)

11、小结一下

以上讲述的,就是 TCP 协定的核心思想,下面过程中须要传输的信息,就体现在 TCP 协定的头部,这里放上最常见的 TCP 协定头解读的图。

不晓得你当初再看上面这句话,是否能了解:

TCP 是面向连贯的、牢靠的、基于字节流的传输层通信协议。

“面向连贯、牢靠”,这两个词通过下面的讲述很容易了解,那什么叫做基于字节流呢?
很简略:TCP 在建设连贯时,须要通知对方 MSS(最大报文段大小)。
也就是说:如果要发送的数据很大,在 TCP 层是须要依照 MSS 来切割成一个个的 TCP 报文段 的。
切割的时候我才不论你原来的数据表示什么意思,须要在哪里断句啥的,我就把它当成一串毫无意义的字节,在我想要切割的中央咔嚓就来一刀,标上序号,只有接管方再依据这个序号拼成最终想要的残缺数据就行了。
在我 TCP 传输这里,我就把它当做一个个的_字节_,也就是基于字节流的含意了。

12、写在最初

一提到 TCP,可能很多人都想起被三次握手和四次挥手所摆布的恐怖。
但其实你跟着本文中的思路你就会发现,三次握手与四次挥手只占 TCP 所解决的外围问题中很小的一部分,只是因为它在面试中很适宜作为知识点进行考查,所以在很多人的印象中就如同 TCP 的外围就是握手和挥手似的。
本文心愿你能从问题登程,真正了解 TCP 所想要解决的问题,你会发现很多原理就如同生存常识一样顺其自然,并不简单,心愿你有播种~
最初,如果对 TCP 的了解仍存在纳闷,能够持续浏览以下精选的材料:

  • 《TCP/IP 详解 – 第 17 章·TCP:传输控制协议》(* 举荐)
  • 《TCP/IP 详解 – 第 18 章·TCP 连贯的建设与终止》
  • 《TCP/IP 详解 – 第 21 章·TCP 的超时与重传》
  • 《通俗易懂 - 深刻了解 TCP 协定(上):实践根底》(* 举荐)
  • 《通俗易懂 - 深刻了解 TCP 协定(下):RTT、滑动窗口、拥塞解决》
  • 《实践经典:TCP 协定的 3 次握手与 4 次挥手过程详解》
  • 《实践联系实际:Wireshark 抓包剖析 TCP 3 次握手、4 次挥手过程》
  • 《网络编程懒人入门 (一):疾速了解网络通信协定(上篇)》
  • 《网络编程懒人入门 (二):疾速了解网络通信协定(下篇)》(* 举荐)
  • 《网络编程懒人入门 (三):疾速了解 TCP 协定一篇就够》(* 举荐)
  • 《脑残式网络编程入门 (一):跟着动画来学 TCP 三次握手和四次挥手》

本文已同步公布于“即时通讯技术圈”公众号。

▲ 本文在公众号上的链接是:点此进入。同步公布链接是:http://www.52im.net/thread-3339-1-1.html

退出移动版