TCP 部门来了个实习生

TCP 部门来了个实习生,一上班就追着老司机问:握手为什么非要三次?数据怎么发才靠谱?关个连接怎么比打开还麻烦?今天这杯咖啡怕是喝不安稳咯。

分享
TCP 部门来了个实习生

新人拜访

“您好,我是 TCP 服务的实习生,刚培训完,请问您就是 TCP 老司机吗?”

正坐在办公室悠闲喝着咖啡的 TCP 老司机,差点一口喷出来。“哦哦,行,你终于来啦,看来几天前的资源申请通过了啊,老司机?”

“浏览器老大这么叫你,说你厉害,靠谱得很。”

“好吧,既然来了,赶紧准备一下!这是你的工作台。” 老司机放下手中的咖啡,走到靠窗的工作台旁,指了指。

“好嘞。” 说着,刚来的小伙子走了过来。

工作台

“好多工具啊!这个通道是什么?这儿还有个钟?都怎么用呀?” 小伙子满脸好奇地问道。

“这样吧,从左到右,我一一给你说说。”

“首先,最左边呢,是个通道,这里会蹦出应用层需要我们发送的数据。最常见的就是 HTTP 那小子的报文了。”

“了解,我们的工作就是发送应用层的数据。原来不需要我们自己去拿啊!”

“旁边是扫描机。你要根据实际情况处理数据,它会把数据分段、编号,再放进旁边的盒子里。”

“嗯嗯,这个我清楚,数据块大小需要根据实时的网络情况确定。”

“不错,这小伙子脑子还挺灵光,看来培训得不错。” 老司机心中暗暗赞道。

“再往右又是一个通道,用来和服务器通信。通道旁有两个计数器,分别记录双方的序号,它们会自动累加,不用手动操作。就叫 1 号计数器和 2 号计数器吧,等下会用到。”

“好的,1 号计数器其实就是报文中的序号?”

“嗯嗯,不错,用的时候看一眼就行,在计数器旁边有几个按钮,我给你说说。这里最好记一下。”

“嗯嗯。” 说着,小伙子拿出笔记本开始记录。

任务降临

老司机指了指标着 SYN 的按钮,说道:“这个是请求连接按钮,也就是通知服务器,我们想发数据给他。”

说着,最左侧的通道突然一闪,蹦出一个由 HTTP 服务打包好的包裹。刚来的小兄弟显得有点慌乱,咽了一口气。

“正好,那我就拿它给你演示一下吧。” 说着,老司机站到了工作台正中。

“嗯。” 小兄弟紧张得说不出话来。

“首先呢,按一下 SYN 按钮,请求发送数据。” 1 号计数器跳了一下,由原本的 0 跳到了 1。“按下这个按钮,工作台会发送一个请求连接报文,相信报文的内容你应该清楚吧?” 老司机问道。

SYN1,序号为 0。” 看了一眼 1 号计数器,小伙子自信地答道。

“嗯,不错!” 不一会,计数器旁的通道蹦出了一段报文,2 号计数器直接由 0 跳到了 11

“让我们来看看都有什么,你看这里。” 老司机指了指报文中的 ACK,小伙子也靠了过来。

ACK 等于 1,说明服务器确认了我们的连接请求,对吧?” 小伙子说道。

“是的,那我们建立连接?” 老司机露出了狡猾的笑容。

“还需要核对 确认序号 吧?” 小伙子有点困惑。

“为什么?” 老司机反问道。

“确保连接的准确性。老师说过,网络是一个复杂的环境,会出现报文滞留的情况,因此 TCP 连接两端规定,在得到请求连接时,需要将 确认序号 置为请求序号加 1。那么现在我们应该还要核对 确认序号1 号计数器的值!” 小伙子转眼看向 1 号计数器,“咦,怎么一样?应该是差 1 的啊?”

“你忘了,在你按下 SYN 按钮时,1 号计数器已经跳了一下吗?”

“哦 ~” 小伙子恍然大悟,“那是应该相等,这个也太好用了吧!得记一下!”

“唰唰唰”,一旁传来铅笔摩擦纸面的声响。“真像我刚来的时候。” 老司机不禁感叹道。

“接着,你看 SYN 字段为 1,说明服务器请求连接,TCP 是一个稳定的连接,双向数据的通道,需要双方都确认连接状态,这点我不需要特殊说明吧?”

“嗯嗯,在学校老师说过。”

“那好,为了确保连接正确,我们也要确认服务器的初始序号:服务器返回的序号是 10,我们就在确认序号中填写 11。实际报文不用手动生成,确认可以建立连接后,按一下 ACK 按钮就行。” 说着,老司机按下了 ACK 按钮。“现在三次握手结束,连接已经建立。我们开始传输数据,剩下几个按钮等用到时再说。”

“那个,我想问一下,我知道 TCP 是个双向数据通道,但如果我们不确认建立连接,直接发送数据应该也没问题吧?我们和服务器都已经发送了一次请求,并且都收到了,为什么还要确认一下呢?” 小伙子若有所思道。

“既然是双向数据通道,那么通道的两端都应该清楚自己和对方的发送和接收的能力,对吧?”

“当然!不然通道是不稳定的。”

“那你想想,在前两次通信过程中,双方都知道了哪些情况?”

“第一次我们发报文,第二次服务器返回报文我们接收到,那我们就知道了我们有能力发送和接收,服务器也有能力接收和发送。”

“那服务器知道什么呢?”

“服务器的话,应该知道了他自己有能力接收,知道我们有能力发送,他自己却不清楚他能不能发送,也不知道我们是否能接收!因为我们还没回复他。”

“因此,三次握手让双方都确认了连接请求和确认报文能够往返,并同步了初始序号,这对可靠的双向数据传输至关重要。” 老司机解释道。

“嗯嗯,我记一下。” 又是一阵 “唰唰唰” 的声音。

发送报文

“已经建立了通道,那接下来就简单了。” 老司机拿起 HTTP 扔过来的数据,说道:“我们看看一次能发送的数据包大小,在这儿。” 说着,指了指工作台最右侧的屏幕,屏幕上显示着一连串的信息,“这些就是系统参数,这个就是我们网络所能承受的最大发送量。”

“嗯,一次可以发送 1100 字节。” 小伙子边看边说道。

“嗯,不错。那我们就按 1000 字节分段吧,给各层首部留出空间。” 说着,老司机启动一旁的扫描机,把分段大小设为 1000,再放入 HTTP 的数据。一旁的盒子里很快出现了排好序的数据块。

“好神奇啊,学校里都我们自己切的。” 小伙子双眼中冒着金光。

“接下来发送报文就好了。”

“好的,我来拼接数据,源端口:5800,目标端口:80,序号:1,数据偏移:20,标志位:无特殊情况,窗口大小:3,校验和:...” 小伙子突然叽里咕噜一堆,吓了老司机一跳。

“不需要这么麻烦,源端口,目标端口,在工作台接收到应用层数据后,就能生成,至于序号和偏移量,你也不用关心啦,我来演示一遍,看好。”

老司机瞧了一眼服务器先前返回的报文,得知窗口为 3,拿起盒子里的数据就往按钮旁的通道里丢,一连丢了 3 个。1 号计数器也连续跳了 3 下:从 11001,再到 2001,最终定格在 3001

“好了,接下来就等服务器响应了。”

“这就好了?” 小伙子明显有点不敢相信。

“对啊,好了!等服务器确认就行。” 老司机有点得意。“工作台会帮我们把数据打包生成报文,这个工作台可是我的专利!”

“哇塞,老司机就是厉害!比学校里高级多了。” 小伙子双眼中再次亮起金光。

单向关闭

没过多久,2 号计数器连跳 3 次,哒哒哒,通道里蹦出 3 个服务器返回的报文,内容都为:请求完成,需要下一份数据。

老司机拿起盒子里的下 3 块,往通道里扔。1 号计数器哒哒哒,又连着跳了 3 次。如此循环往复了十几次,数据终于发完。

“今天的网不错诶,没有重发的情况出现。现在我们发完了,只需要等待服务器发送数据就行了。”

大概过了 10ms,通道内陆陆续续开始蹦出报文,老司机刚想上手,一旁小伙子一把把报文揽走,“这个我熟,我来吧!”

小伙子取出报文数据,按序号排好,一套操作相当熟练。

哒哒哒,2 号计数器不断地跳动,定格在 50001 上。

“好,现在数据也接收完毕了,需要进行单向关闭。该怎么做?” 老司机想考考小伙子的能力。

“发送报文,将 FIN 置为 1,序号为 43001。” 小伙子瞧了一眼 1 号计数器说道,“然后等服务器返回确认。哦,对了,为了避免把滞留的旧报文当成新报文,还要核对确认序号。”

“不错,但在这……” 老司机话还没说完,小伙子便兴奋地问:“是不是按那个 FIN 按钮就好了?”

“嗯,对!我们只需要核对 确认序号 就好了。你来操作吧 ~” 看着一脸兴奋的小伙子,老司机停下了手中的操作。

“好的。” 说着,小伙子按下了 FIN 按钮。1 号计数器跳了一下,定格在 43002。不一会儿,通道里蹦出报文,确认序号与 1 号计数器一致,都是 43002

“现在单向关闭成功,等待服务器请求关闭。” 小伙子一副训练有素的样子。

“嗯,不错,接下来就等待服务器最终确认关闭了。” 老司机拿起一旁的咖啡,慢悠悠地问:“那你知道为什么这是单向关闭吗?”

“因为只有我们这边确认把数据都已经发送完毕啦 ~ 没准服务器还有数据给我们呢 ~ ”

“嗯,不错。” 老司机拿起咖啡喝了一口,不禁感慨道:“后继有人了啊!”

“老大,服务器发出关闭请求了,你看。” 小伙子拿起一个报文说道。

“我想你应该知道怎么做了吧?”

“嗯嗯,按 ACK 按钮就好了,这个和开启连接确认是同一个操作,按您的智慧,应该是这样。” 说着按下了 ACK 按钮。

“嗯,这个彩虹屁真香,哦不,你真聪明!”

“哈哈!” 两人同时笑出声。“我和你说,那个 HTTP 服务部可不咋的,经常砸我,这不数据都好了,砸 TM 的。”

duang ~” 通道一头发出一阵巨响,“TCP 你们没完了是不?”