本文主要是介绍《趣学CCNA——路由与交换》一第2章 TCP/IP协议2.1 TCP协议简介,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
本节书摘来自异步社区《趣学CCNA——路由与交换》一书中的第2章,第2.1节,作者 田果 , 彭定学,更多章节内容可以访问云栖社区“异步社区”公众号查看
第2章 TCP/IP协议
趣学CCNA——路由与交换
在上一章,我们郑重其事地介绍了无聊的OSI七层参考模型,并浓墨重彩地讲述了其中每一层负责提供的功能。OSI模型出身名门、条理清晰,只有一个“小小的”缺点,那就是一直没人太拿它当回事儿。所以,如果对它太认真,你就败了。
我们是有职业精神的,因此在介绍OSI模型时反复强调了这个模型是如何地曲高和寡。我们在上一章中花大篇幅介绍OSI模型有三个目的:一是延续各类技术教材的惯例,以免将本书作为技术开蒙读物的读者在与别人讨论技术问题时,因全然不了解OSI模型而被视为十分业余;二是为了让这本冠以“CCNA教材”之名的作品能够勉强契合Cisco认证体系的教学大纲,不至于洋洋千言,却离题万里;第三,也是最为重要的一点,是为了让读者能够凭借这个将功能划分得最为细致的模型,深入理解“协议分层”的概念及其存在的必要性。
除了OSI模型之外,我们在上一章还介绍了另外一个更切合实际的分层模型:TCP/IP模型。传统的TCP/IP参考模型定义了四个层级,从下至上依次为网络接口层(第1层)、网络层(第2层)、传输层(第3层)和应用层(第4层)。
读者可以通过图2-1回忆OSI模型与TCP/IP模型之间的对应关系。
注意:
图2-1中TCP/IP模型各层的命名方式很火星?其实,TCP/IP 模型中的各层还有另一套命名方式,这套命名方式来自于 TCP/IP 模型的前身,称为 DoD 模型 。DoD模型其实颇有一些历史可以介绍,但如今按照DoD的命名方式提及TCP/IP模型诸层的业界人士已然越来越少,我们也不打算在这里多费笔墨。为免读者在读过本书之后对TCP/IP模型中各层的另一套命名体系一无所知,我们在图2-1中直接引用了某幻灯片中的称谓。但在本书后文中,为了统一起见,我们将继续使用最为常用的命名方法。图 2-1 中的命名体系 有可能会出现在 CCNA 的考试中,读者也要留意理解 。
TCP/IP模型与OSI模型的最大区别之一是,TCP/IP模型并不是一个凭借想象力设计出来,然后就交给人们去套用的理论模型,而是对已有的TCP/IP协议栈所进行的描述。TCP/IP协议栈是诸多网络互联协议的集合,并以其中最基础的两个协议(TCP和IP)进行命名。除了这两个核心协议之外,TCP/IP协议集中还包含了N多其他的协议。这些协议协同工作,构建了基本的网络互联框架,为不同应用层协议实现各自的目标打下了通信基础。
应用层的协议相当丰富,就连许多网络知识水平不高于文盲的普通用户也对一些应用层协议耳熟能详。比如在门户网站上浏览艺人花边新闻时使用的HTTP协议,在网上银行交易纸黄金直到第二天上班的公交车钱都被套牢时使用的HTTPS协议,偷偷下载各类格调不甚高雅的视频时使用的FTP协议,皱着眉头参照家用宽带路由器说明书进行配置的DHCP协议,都属于应用层协议。这些贴近最终用户的协议,是希望成为系统工程师的读者应当着意钻研的课题,但它们并不是思科路由交换认证体系中的重点,在我们这里也不准备作为重点进行介绍。我们这一章的重点是对工作在下面三层同时又对网络通信发挥着至关重要作用的五大协议进行介绍,它们是TCP协议、UDP协议、IP协议、ICMP协议和ARP协议。
先抑后扬,让我们从最复杂的TCP协议说起。
2.1 TCP协议简介
趣学CCNA——路由与交换
上一章介绍传输层的时候曾经提到过,传输层的某些协议是“面向连接”的,所谓面向连接是指这个协议可以追踪数据的传输状态,并且可以在传输失败的时候对数据进行重传 ,而本节要进行介绍的TCP 协议就是面向连接的协议。 TCP 协议的全称是“传输控制协议”,这种协议可以给上层应用提供一种可靠的传输机制 。用人话来说,就是通过这家快递公司发件,它可以通过追踪包裹的实时状态来监控投递进度,就算对方因为某种原因没有收到,它们也会再次上门投递。
2.1.1 TCP的头部格式
在第1章的1.4节中,我们按照TCP/IP 五层模型对数据从应用程序发送的消息转化为比特流的过程进行了解释。从本章开始,读者就要开始深入了解“封装”的操作方法。为此,我们需要先“引见”几个基本的术语。
既然称为“封装”,那么这个数据在经历各层的处理时,会由相应的协议在这个数据“周围”添加一些“补充说明”,以便接收方在解封装时看到这些补充说明,而采取一些相应的功能。当然,设备给数据添加“补充信息”时并不能任性,它必须参考相应的协议标准,而这些协议自然也都定义了添加补充信息的格式。虽说是在“周围”添加数据,但数据毕竟只有长度这一个维度,因此所谓的“周围”也只包括前、后两个位置。如果补充信息添加在了原数据的前面,这些信息就称为“头部”,如果添加在原数据的后面,则称为“尾部”。而各个协议所规定的补充信息添加格式,也就称为这些协议的“头部格式”和“尾部格式”。
注释:
如果我们的讨论只局限在路由交换技术领域,而不涉及安全加密领域的话,那么给数据包添加头部信息要比给数据包添加尾部信息的做法常见得多,后者多用于对数据包进行校验。由于校验的功能往往由数据链路层提供,因此封装尾部的做法多发生于数据链路层。“帧( frame )”这个词儿在英文中的原意是“框子”,之所以用它代指经过数据链路层封装后的信息,就是因为这一层在对数据进行封装时会在头尾两侧都添加信息,因此数据链路层输出给下层的信息就变成了一个“夹心儿数据框”。在此我们顺便建议读者,如有可能,请尽量多阅读英文文档,尤其是RFC文档。在大多数情况下,准确地把握住了一个单词,就能串出一条完整的概念体系,如frame→框子→头尾都添加数据→校验→数据链路层的功能→分层体系的理念。
说了这么多,唯一的目的就是为了引出TCP的头部格式。请读者看图2-2。
源端口号和目标端口号
端口号这个概念在本书中第一次出现。这个概念极为重要,值得进行一番介绍。
一次通信的过程,既不以一台设备将比特流发送出去作为开端,也不以一台设备将比特流接收进来作为终结。对于每个参与通信的人来说,电脑接收到的这些比特流毫无意义,人们要的是自己看到、听到之后,转化到人脑里成为脑电波。所以,如果您想知道:网络中有那么多台电脑,传输设备是怎么从万千电脑中找到对方的电脑呢?那么,您就不妨也思考一个极为类似的问题:应用层有那么多的进程,传输层协议是怎么从万千进程中,把这个数据的进程找到的呢?
这个问题很快可以转换为,在整个通信的过程中,谁知道应该把这个数据交付给哪个进程?答案是:谁生成了这个数据,谁就知道应该把它交付到哪里。因此,当传输层协议封装数据时,它就有义务通过某种方法告诉对端设备的传输层协议,这个数据应该最终交给上层的哪个服务去处理。当然,传输层协议没法直接把“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。
注意,在图2-3中,如果左边的小人就这么站在那儿不走,右边的小人就算再英勇也没法单骑闯关,因为魂斗罗的窗口会被左边的小人拖住。换句话说,在魂斗罗游戏中,游戏的进展取决于落在最后的那个人的进展,这可以称之为“魂斗罗版的短板效应”。
回到窗口大小的话题。前面我们通过序列号和确认号说明了一个概念,那就是TCP传输是讲究顺序的,是不能丢的,丢了是要重传的。因此所有数据都有自己的编号,所有编号的数据都要到位。这样一来,当两台设备相互之间建立了TCP连接,一方发送数据,另一方接收数据的时候,收发的双方就可以了解对方发/收数据的进展了。为了实现可靠传输,TCP定义了一个滑动窗口的概念(见图2-4),以便发送方只发送接收方能够接收的信息。
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,那么三次握手的过程可以描述如下。
第一次握手
客户端向服务器发送一个数据包,目的是为了与对方建立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所示。我们假设当客户端希望断开与服务器之
间的连接时,客户端将要发送的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连接。
TCP的学习总算可以告一段落了。虽然在这一章之后,我们还需要介绍足足三个协议。别急着上吊,因为那三个协议的篇幅加在一起,大概也只能占到TCP篇幅的一半。下面,我们来看看和TCP同样工作在传输层的UDP协议。
这篇关于《趣学CCNA——路由与交换》一第2章 TCP/IP协议2.1 TCP协议简介的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!