《趣学CCNA——路由与交换》——2.1节TCP协议简介

本节书摘来自异步社区《趣学CCNA——路由与交换》一书中的第2章,第2.1节TCP协议简介,作者 田果 , 彭定学,更多章节内容可以访问云栖社区“异步社区”公众号查看

2.1 TCP协议简介
趣学CCNA——路由与交换
上一章介绍传输层的时候曾经提到过,传输层的某些协议是“面向连接”的,所谓面向连接是指这个协议可以追踪数据的传输状态,并且可以在传输失败的时候对数据进行重传 ,而本节要进行介绍的TCP 协议就是面向连接的协议。 TCP 协议的全称是“传输控制协议”,这种协议可以给上层应用提供一种可靠的传输机制 。用人话来说,就是通过这家快递公司发件,它可以通过追踪包裹的实时状态来监控投递进度,就算对方因为某种原因没有收到,它们也会再次上门投递。

2.1.1 TCP的头部格式
在第1章的1.4节中,我们按照TCP/IP 五层模型对数据从应用程序发送的消息转化为比特流的过程进行了解释。从本章开始,读者就要开始深入了解“封装”的操作方法。为此,我们需要先“引见”几个基本的术语。

既然称为“封装”,那么这个数据在经历各层的处理时,会由相应的协议在这个数据“周围”添加一些“补充说明”,以便接收方在解封装时看到这些补充说明,而采取一些相应的功能。当然,设备给数据添加“补充信息”时并不能任性,它必须参考相应的协议标准,而这些协议自然也都定义了添加补充信息的格式。虽说是在“周围”添加数据,但数据毕竟只有长度这一个维度,因此所谓的“周围”也只包括前、后两个位置。如果补充信息添加在了原数据的前面,这些信息就称为“头部”,如果添加在原数据的后面,则称为“尾部”。而各个协议所规定的补充信息添加格式,也就称为这些协议的“头部格式”和“尾部格式”。

注释:
如果我们的讨论只局限在路由交换技术领域,而不涉及安全加密领域的话,那么给数据包添加头部信息要比给数据包添加尾部信息的做法常见得多,后者多用于对数据包进行校验。由于校验的功能往往由数据链路层提供,因此封装尾部的做法多发生于数据链路层。“帧( frame )”这个词儿在英文中的原意是“框子”,之所以用它代指经过数据链路层封装后的信息,就是因为这一层在对数据进行封装时会在头尾两侧都添加信息,因此数据链路层输出给下层的信息就变成了一个“夹心儿数据框”。在此我们顺便建议读者,如有可能,请尽量多阅读英文文档,尤其是RFC文档。在大多数情况下,准确地把握住了一个单词,就能串出一条完整的概念体系,如frame→框子→头尾都添加数据→校验→数据链路层的功能→分层体系的理念。
说了这么多,唯一的目的就是为了引出TCP的头部格式。请读者看图2-2。


10766d61a3e269813b9fd5d4106e6a2b42e9415e

源端口号和目标端口号
端口号这个概念在本书中第一次出现。这个概念极为重要,值得进行一番介绍。

一次通信的过程,既不以一台设备将比特流发送出去作为开端,也不以一台设备将比特流接收进来作为终结。对于每个参与通信的人来说,电脑接收到的这些比特流毫无意义,人们要的是自己看到、听到之后,转化到人脑里成为脑电波。所以,如果您想知道:网络中有那么多台电脑,传输设备是怎么从万千电脑中找到对方的电脑呢?那么,您就不妨也思考一个极为类似的问题:应用层有那么多的进程,传输层协议是怎么从万千进程中,把这个数据的进程找到的呢?

这个问题很快可以转换为,在整个通信的过程中,谁知道应该把这个数据交付给哪个进程?答案是:谁生成了这个数据,谁就知道应该把它交付到哪里。因此,当传输层协议封装数据时,它就有义务通过某种方法告诉对端设备的传输层协议,这个数据应该最终交给上层的哪个服务去处理。当然,传输层协议没法直接把“HTTP”四个大写英文字母封在数据的头部,它只能封装代表相应服务的编码,这个编码就是端口号。端口号的使用也是有规矩的,其中0 ~ 1023 的端口号 被分配给了一些固定的协议,如FTP(TCP 20、21)、SMTP(TCP 25)等,这些端口号称为知名端口号,是由 IANA 分配的; 1024 ~ 49150 的端口号可由应用程序动态选用, IANA 对这些端口的使用情况进行了登记; 49151 ~ 65535 是临时端口。

除了扯淡的OSI模型在传输层和应用层之间定义了两个功能冗余的会话层和表示层之外,在现实世界(和TCP/IP五层)模型中,传输层之上只有一层,那就是应用层。因此,端口号就是传输层赋予应用层进程的编号。这句话可以延伸为,所有有端口号的协议全都工作在应用层。

注释:
如果您拥有一点路由技术方面的基础,读到这里时有可能会产生一个关于动态路由协议的疑问:难道拥有端口号的路由协议也是应用层协议吗?事先预告一下,我把自己对于这个问题的理解写在了第10章中(是的,不是第9章)。如果您压抑不住冲动,可以去找找看。对于零基础的读者,不建议提前把这个问题列入思考。
把上面的内容总结起来就是:当传输层协议处理信息时,它会通过源端口号字段说明这个信息是由哪个进程生成的,同时通过目标端口号字段说明这个信息需要由接收方的哪个进程进行处理。

序列号和确认号
在数据从发送方主机的传输层到达接收方主机传输层的这个过程中,有可能会出现乱序的情况,甚至也很有可能丢失。而我们在前面刚刚说过,TCP是一项面向连接的协议,因此它得对这些情况负责。序列号和确认号的作用正在于此。

TCP 为了保证自己发送的每一个字节都可以被对方收到,并且都是按顺序收到的,就必须对每一个字节都进行编码。当然 ,为了保证传输的效率,TCP倒还不至于对每个字节都进行标识,否则网络上传输的编码就比实际数据还多,岂不是本末倒置?事实是,TCP 只会通过序列号来表示这个数据段第一个字节的编码,后面每一个字节的编码也就不言自明了。比如,一个数据段的序列号字段是1117,那么,如果这个数据段一共携带了810字节的数据,它的最后一个字节的编码就是1926,下一个数据段的序号应该从1927开始。

而确认号是接收方设备告诉发送方设备,接下来,它希望收到以哪个序列号开头的数据。如果一个数据段的序列号字段是1117,而这个数据段一共携带了810字节的数据。那么,如果接收方正确地接收到了这个数据段,它就会要求给发送方给自己提供1927开头的数据。换句话说,在正常情况下,此时接收方发送给发送方的TCP数据包,确认号就应该是1927。

首部长度
图2-2画得清清楚楚,TCP头部当中可以根据需要添加一些可选项字段。所以,TCP头部的长度是不固定的。既然TCP头部的长度是多少都不确定,在 TCP 头部中,就需要有一个首部长度字段用来说明 TCP 头部的长度是多长 , 从哪里 开始是 TCP封装 的数据部分。

URG__与紧急指针
URG叫做“紧急位”,当这个位的数值为1时,后面16位的紧急指针就会生效。如果一个数据段的URG位为1,TCP在处理时,就会将这个数据段中的紧急部分插入到数据段的最前面,而紧急指针字段则会指明这个数据段中,紧急数据部分的长度。由于紧急部分位于数据段最前面,因此知道了紧急部分的长度,就知道了正常数据的起始位置。当TCP优先处理完紧急数据时,就会以正常操作的形式再去处理后续的数据。

ACK
ACK 叫做“确认位”,当这个位的数值为 1 时, 32 位的确认号才会生效。由于TCP提供的是可靠传输,而确认位在保障可靠传输的体系中居功至伟,因此几乎所有TCP封装的头部都会将ACK位置于1,例外情况详见后面的TCP连接的建立过程,这里暂时卖个关子。

PSH
当PSH位的数值为1时,接收方不会将这个数据段放在TCP缓存中等待其他数据,而会立刻将数据包提交给用户进程。

RST
当RST位的数值为1时,相当于TCP要求对端不经“四次握手”的过程,立刻断开TCP连接。关于TCP连接的断开过程,我们也会在后面单独介绍。

SYN
当 SYN 位的数值为 1 时,表示这台设备希望与对端建立 TCP 连接 。

FIN
当 FIN 位的数值为 1 时,表示这台设备希望与对端断开 TCP 连接 。

校验和
校验和的作用是检验数据信息在传输的过程中是否出现过变化。

窗口大小
窗口大小这个概念是所有同类图书作者的噩梦,不动用两页纸左右篇幅、一整套抽象图形和大量纯理论术语,想把这个概念说清楚几乎就是天方夜谭。YESLAB敢为天下先,尝试用一种不算那么理论的解释方法,来简单地介绍一下这个窗口这个概念的大致含义。

作者小时候,是和父亲一起彻夜玩儿红白机双人魂斗罗长大的。在双人魂斗罗中,玩游戏的两个人是合作关系而不是竞争关系,但是遇到天上不时飞过的散弹枪,两个玩儿游戏的人也经常会因为分赃不均而起争执。可总的来说,在玩双人魂斗罗的两个人相互之间还是会尽量与对方合作,以避免摩擦,否则万一一方真的恼羞成怒,后果也很麻烦,因为任何参与游戏的人都有一种很恶劣的耍赖方式,可以让另一位玩家也没法继续进行游戏,那就是——赖着不走,请看图2-3。


839ecab2e0fffd34466aca438fd0da0d2878d4fa

A是发送方的设备,我们可以看到它发送了序列号为300~309的数据,其中300~303已经收到了确认,304~309没有得到确认。310~316已经可以发送但是还没有发送,而317之后的数据还不能发送。如果此时A从B那里接收到了对304数据的确认,滑动窗口就可以相应地向前滑动到317,这就是说,A也可以对317数据进行发送了。对于A来说,已发送但还没有收到确认的那些数据就像是拖在最后的那个魂斗罗,它拖住了窗口向前移动,而TCP窗口的最前端也就相当于魂斗罗窗口的最前端,那是在最左边的小人移动之前,TCP这个游戏最多可以继续发展的情节。

B是接收方的设备,它对自己接收到的300~303的数据向A进行了确认,同时它也接收到了306~308的数据,但它没有对这些数据进行确认,因为304和305没有收到。滑动窗口允许B接收序列号316及以前的数据,但304和305也像魂斗罗窗口中左边小人一样拖住了这个窗口。好在316之后的数据,A倒是暂时也还不能发送。

除了端口号之外,我们对于其他字段的解释基本只能称为简介。在CCNA阶段,读者不需要对这些头部字段进行过于深入的了解。之所以介绍滑动窗口机制和介绍其他头部字段,都是为了帮助读者了解TCP是怎么为数据提供一种可靠的传输机制的。同时希望读者能够理解,在通过 TCP 传输 数据的过程中,通信双方之间会交互一些负责保障数据有序、完整、未经修改的信息。

在前文中,我们尽量本着有话则长、无话则短的原则,对TCP协议定义的数据头部各个字段进行了简要的说明。常有一些看美剧的人,连续三集意思不大就会弃剧。这种做法也常常被移植到了读书上来,只不过单位从“集”换成了“段”。无奈的是,连我自己也知道, TCP数据包的字段分析有可能成为(继OSI模型各层介绍之后)本书被弃的第二个高峰,如果您觉得无聊但并没有直接弃书而跳读到了这一段,我向您表示由衷的感谢,作为对您的回报,我在这里提供给您以下四点信息。

关于数据包头部字段的介绍确实乏味,甚至并不是CCNA级别所要求的范畴,但这些数据包头部是协议赖以实现其功能的“定义级”信息,而本章即将介绍的这四个协议(尤其是头三个协议)又是当今互联网的“定义级”协议,重要性不言自明。
图书作者的稿酬仅与您是否购买本书有关,与您是否使用,以及如何使用本书无关。这句话翻译成现代汉语就是:您只要买了这本书,您的钱我们就笑纳了,您觉得不好看我们也不退钱。所以说,反正您钱都花了,不看下去多不合算。
如果您实在不喜欢阅读理论性过强的内容,或者认为这些信息过于抽象,可以把书中这些枯燥的信息当成工具类信息使用,不通篇逐字阅读,仅简略浏览,在后面阅读的过程中遇到问题时再来有目的地重读。第 1 章对 OSI 模型各层的介绍也适用这种做法。
我们马上要介绍的内容是TCP连接建立与断开的过程,我悲痛地通知您,设计这个过程的人也没有在他们的设计理念中融入太多的幽默元素。

2.1.2 TCP连接的建立
介绍TCP的头部格式,目的无非是为了说明它的工作方式,下面我们结合上面介绍的一部分内容,特别是其中的几个标记位,来介绍TCP的工作流程。

TCP既然称为面向连接的协议,必然就会在通信之前先建立连接。总的来说,一个TCP连接通常会经历三个阶段,分别为连接建立、数据传输及连接释放。我们先从TCP连接的建立过程说起。注意,为了简化叙述,在这个过程中,我们只把重点放在与连接建立相关的序列号、确认号及相应的标志位上,不考虑连接的端口号与其他头部字段。

如图2-5所示,TCP连接建立的过程常常称为“三次握手”。假设客户端要和服务器建立TCP连接,而三次握手就是客户端与服务器之间交换ISN(初始化序列号)的过程。假设客户端的ISN值为x,服务器的ISN值为y,那么三次握手的过程可以描述如下。


fb74977a35d8ecbcfc9827304f83205410574aaa

第一次握手
客户端向服务器发送一个数据包,目的是为了与对方建立TCP连接。为了说明这个意图,这个数据包会在头部字段中说明以下几点。

我希望和你建立TCP连接(将头部的SYN标记设置为1)。
我把我的初始序列号提供给你(将序列号字段的值设置为自己的初始序列号值x。注意,我们之前曾经说过序列号表示数据的第一个字节,但TCP规定,TCP连接建立的第一个SYN数据包不能携带数据部分,但它也会占用一个序列号)。
注意,这个数据包头部的ACK字段的值就是0,因为这是两边的第一次交流,没什么先前的信息可以确认。

第二次握手
服务器收到了客户端发送的数据包,同意与客户端建立连接。这时,服务器也会向客户端发送一个数据包。这个数据包旨在说明下面几点。

同意和你建立TCP连接(将头部的SYN标记设置为1)。
你发来的请求信息已阅,期待收到你的下一个数据包(将ACK标记设置为1,确认号的值设置为x+1)。
我也把我的初始序列号提供给你(将序列号字段的值设置为自己的初始序列号值y,注意,TCP又规定了,这也是个TSYN数据包,所以这个数据包也不能携带数据部分,但它也会占用一个序列号)。
第三次握手
客户端在收到服务器发送的数据包后,知道服务器同意与自己建立TCP连接,并与自己交换了初始序列号,此时它会向服务器发送三次握手过程中的最后一个数据包,这是为了告诉对方下述信息。

你发来的答复信息已阅,期待收到你的下一个数据包(将头部的ACK标记设置为1,确认号的值设置为y+1)。
这是根据你的要求发给你的下一个数据包(将序列号值设置为x+1。注意,这个数据包不再是SYN数据包了,因此它如果不携带数据,就不会占用序列号,这里我们姑且假设它携带了数据)。
显然,三次握手是通过TCP传输数据的前戏。连接建立之后,双方就可以开始在这个连接的基础之上传输数据了。那么,数据传输完成之后呢?

2.1.3 TCP连接的断开
在完成数据传输后,需要通过一个更加复杂的“四次握手”流程来断开TCP连接。下面我们趁热打铁,介绍一下TCP连接断开的过程。

TCP连接断开的整个过程如图2-6所示。我们假设当客户端希望断开与服务器之


da2a78654565d156c722646a93f5162dd8b305fd

间的连接时,客户端将要发送的TCP数据包序列号为k,用于确认服务器之前传输的数据包的确认号为l,那么此时TCP连接断开的过程就可以描述为下面的四次握手。

第一次握手
客户端向服务器发送一个数据包,这个数据包的目的是告诉服务器,自己希望与它断开连接。为了说明这个意图,这个数据包会在头部字段中说明以下几点。

我收到了你刚才发来的消息,期待你的下一条消息(将ACK标记设置为1,确认号的值设置为l)。
我现在希望和你断开TCP连接(将头部的FIN标记设置为1)。
我把这个消息序列号提供给你(将序列号字段的值设置为k。注意,TCP再次作出规定,FIN数据包可以携带数据,且无论是否携带数据,均占用一个序列号。我们在这里假设,TCP连接断开阶段的四次握手数据包均不携带数据)。
第二次握手
服务器收到客户端发来的断开连接请求之后,断开了客户端到服务器的连接,并向客户端回复一个TCP数据包,这个数据包的目的是告诉客户端下述信息。

我收到了你发来的连接断开请求,期待你的下一条消息(将ACK标记设置为1,确认号的值设置为k+1)。
告诉你这个消息的序列号(将序列号设置为l)。
第三次握手
这一次还是服务器发送给客户端的消息,这个消息的作用是为了请求客户端断开客户端到服务器的连接,在这个消息中,服务器表示:

我收到了你之前发来的连接断开请求,期待你的下一条消息(将ACK标记设置为1、确认号的值设置为k+1,因为第一次握手之后,客户端不会再向服务器发送数据了);
我现在希望和你断开TCP连接(将头部的FIN标记设置为1);
我把这个消息序列号提供给你(将序列号字段的值设置为ll。因为虽然第一次握手之后,客户端就不会再向服务器发送其他数据了,但第二次握手之后,服务器只断开了客户端到服务器的连接,服务器与客户端的连接依然存在,因此在与第三次握手之前,服务器可能还会给客户端发送一些其他占用序列号的消息)。
第四次握手
客户端收到服务器发来的断开连接请求之后,也断开了服务器到客户端的连接,并向客户端回复一个TCP数据包,这个最后的数据包对服务器说:

我收到了你发来的连接断开请求(将ACK标记设置为1,确认号的值设置为ll+1。注意,即使在这条消息中,客户端仍会向服务器提供确认号);
告诉你这个消息的序列号(将序列号设置为k+1)。
上述过程就是TCP连接建立与断开的完整步骤,这个步骤对于此后的学习至关重要,无论路由交换方向、安全方向还是SP方向。不过,这个过程倒是不用在这里就开始死记硬背,我们推荐读者在这里先把上面的两张连接建立示意图看个“眼熟”,以此来熟悉TCP连接建立与断开的过程。

在本节最后,我们通过表2-1向读者介绍几个常用的基于TCP的应用层协议。在我们平常使用这些协议时,数据都会在经过传输层处理时封装上那样的TCP头部,也都会与对端的设备按照上面的过程建立和断开TCP连接。


7bcfb9c609390760060bde2fb80746ecf57ea93b

TCP的学习总算可以告一段落了。虽然在这一章之后,我们还需要介绍足足三个协议。别急着上吊,因为那三个协议的篇幅加在一起,大概也只能占到TCP篇幅的一半。下面,我们来看看和TCP同样工作在传输层的UDP协议

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值