计算机网络知识解析

2,273 阅读43分钟

文章首发于公众号: 吴先生的伊甸园   


本篇文章主要讲的是计算机网络相关的内容,需要有一定的计算机网络的基础知识才能汲取更多的知识。当然没基础也可以看懂,会对计算机网络有一个基础的理解。在这一篇介绍中,我尽可能的覆盖一些面试中的问题,通过本篇文章,无论你是实际开发,还是准备面试的过程中,都能对计算机网络都有一个比较深入的理解。

文章内容时不时会追根问底,比如三次握手的过程是什么,深入一点的问题就是为什么需要三次握手,而不是俩次握手,诸如此类的模式。文章可能会比较长,但是抽出半小时到一个小时的时间来读此篇文章,会让你理清计算机网络的相关知识。

本篇文章只会讲到网络层之上,网络层之下的数据链路层和物理层本文不做深入的讨论,毕竟在往下属于便硬件相关的内容了,而不是软件开发相关的内容。

文章的主要内容有:

  1. 计算机网络概述

    1. 应用层

    2. HTTP

    3. Socket

    4. 传输层

    5. TCP

    6. UDP

    7. 网络层

      • IP
      • DHCP
      • ICMP

计算机网络概述:

计算机网络是通信技术与计算机技术结合的产物,也就是说计算机网络就是为了解决计算机与计算机之间通讯的问题。什么是通讯的问题,就是数据交换的问题,也是信息交换的问题。在现实世界中,我们用信息来形容交换的内容,在计算机世界里,我们用「数据」这个词来代替。这些词都是对内容的抽象概括,因为现实世界需要交换的内容太复杂了,一段文字称之为信息,一张图片也叫信息。所以这些词都是对这些内容的抽象概括,其实完全没有那么高大上,意会了就好。

继续讲,我们知道,与计算机网络相关的东西有哪些,大家可以列举出很多,比如网线,网卡,路由器,计算机中的 IP 地址。专业一点的人知道 TCP UDP,HTTP ,FTP。这些东西分别对应着计算机网络中不同的层次。层次有不同的分法, OSI(网络)模型将计算机网络分成了七层(搜索关键词 OSI 七层模型),因为分的太多太细,跟现实生活中操作有一些不匹配,被我们称为理论上的成果,市场上的失败,但却是我们学习计算机网络的好工具。

在市场上成功的方式是将计算机网络分成五层或者四层。我喜欢按五层来分。刚刚列举出关于计算机网络的内容中,网线属于物理层,网卡属于数据链路层,路由器属于网络层,对应的协议有 IP ,ICMP等。最后一个字母 P 代表 Protocol 协议的意思,因为狗血的语言不合的问题,我们还时不时的称之为 IP 协议,HTTP 协议等,重在理解,叫什么就无所谓了。

还有计算机网络拥有的是一个体系结构,分成那么多层是因为计算机网络体系太复杂了,还涉及到各种各样的组成部分,一次性规范这么多内容不太现实,所以我们按不同设备按照功能划分成不同的层次。这样做的好处是每一层对其他层来说都是透明的,更利于标准化。某一层变化了,不影响其他层的工作。

分层思想在计算机领域应用的还是比较多的。分层带来的好处就是透明,更容易制定标准。如何理解透明这一概念,透明的含义就是我不需要知道你是怎么工作的,我想要什么你能给什么就行。最简单的例子,我玩手机不需要知道手机是怎么工作的,我只要会点屏幕就行,我能处理你给我展现的内容就行。屏幕显示的内容就是手机提供给我们的内容。但是内部电池如何供电,供多大的电流,我们不需要考虑。这就是透明的含义。

在计算机网络中也是如此,不需要理解上层和下层是怎么工作的,我只需要接受下层给我的数据,并且我能看懂,经过我这层之后,我按照上层在一开始规范好的数据格式,提交给上一层,上一层就会能正确的接收我提交的数据。分层之后,某一层的修改不会影响其他层。怎么理解呢,IPv4 和 IPv6 都处于网络层,属于不同版本的协议,但是从 IPv4 切换到 IPv6 对于应用层的 HTTP 来讲是没有区别的,HTTP 不需要管你用的是 IPv4 还是 IPv6,你按照我这 HTTP 的格式传上来数据就行。就是这个意思。

应用层

应用层有着不同的协议,每个协议也都有不同的功能和用法,在这一层协议是最丰富的,但也更佳具体。比如 HTTP 用于我们网页内容的传输。FTP 用于文件的传输。SMTP 用于邮件的传输,还有 TELNET等等。每一个协议都比较复杂,都有详细的规定,对协议的描述也比较细致,实现的功能也比较多,所以就不在这里写了。如果你有需要,可以定向的了解某一个协议的详细内容。

Socket

在应用层和传输层之间有一个接口叫做 Socket。Socket 不属于某一个层,它是就像是应用层和传输层之间一个管道,用来连接操作系统和应用层中的具体的应用进程。应用可以操纵 Socket 来使用操作系统的网络功能。Socket 翻译过来叫「套接字」。很奇怪的名字。因为传输层之下一般都是由操作系统来控制,而应用层的一些协议是由应用进程来控制的,所以我们需要一个接口来建立起应用进程和底层协议的桥梁,这个接口呢叫做 API(应用编程接口)。UNIX 定义了一个具体的实现,叫做 Socket。

现在大多数操作系统都支持 Socket。微软也做了一个改进的API,叫做 winSock。其实就是 API 在不同操作系统上,定制化开发的一个 API,不知道你们理解了没有。还要区分的是现在火起来的 WebSocket。WebSocket 是应用层的协议。而 Socket 不属于某个层,实现的是进程跟操作系统在网络通讯方面的接口的具体实现。因为接口指的是 API,具体实现指的是 Socket。我们可以面向 Socket 编程,也就是自己定义了一个传输协议来实现计算机双方的通信。

就比如说你觉得 HTTP 太复杂了,状态码缓存什么的太臃肿了,我要自己写一个程序,其实你只需要使用 Socket 发送和接收数据,用代码来处理传输过来的数据,完全就完全能满足计算机间的通信问题,根本不需要在遵循其他应用层的协议了。你创造的数据处理规则就可以称之为自定义的协议。

你写的这个程序运行起来,操作系统就会自动分配给你一个端口号,在加上本机的 IP 地址,就能唯一的确定你这个程序的进程,等操作系统接收到发给你这个程序的数据,地址为 IP:分配的端口号 ,就会找到你的这个进程,把数据提交给你,进程就按照程序的逻辑来处理数据了。端口号是你使用 Socket 的一些函数时自动分配给你或者你手动配置的。那大家知道那么多端口号是怎么来的了吧,就是为了区分不同的进程,用于通讯的。像一些 Web server ,如 Nginx,肯定也利用了 Socket ,才能绑定了80端口。

传输层

传输层最典型的俩个协议是 TCP 和 UDP。当然还有其他的,如果好奇的可以去了解一下,比如 DCCP,SCTP 等。传输层提供的是应用进程之间的逻辑通信机制。怎么理解呢,我们知道电脑上运行着不同的进程,而在传输层,就要区分出这个数据是送到哪一个进程的。靠的是什么呢,靠的就是端口号。一个网络进程一定会使用 Socket,使用 Socket 就会随机分配一个端口号,或者利用 Socket 的绑定函数,手动把某一个端口号绑定到这个进程上。

多路复用,传输层有一个 TCP 和 UDP 多路复用的说法,这跟物理层中的多路复用不太一样,物理层的多路复用指的是物理线路上的复用方式,包括频分复用,时分复用,码分复用,波分复用(与频分复用属于一个类型,波长与频率是有关系的),而在传输层中讲的多路复用是多个数据报同时接收,计算机是如何处理这些数据包的,是如何分发,按什么原则分发,传输层的多路复用讲的是这个。在传输层之下,所有数据包都经历了相同的处理,但到了传输层不同的数据报传输就有了区别。「所有」其他主机给我发送的「所有」UDP 数据报都会被提交给同一个端口。而根据连接的不同会将 TCP 数据报分给不同的端口。因为 TCP 连接是一对一的,每一个端口只对应一个连接,也同时对应另一台主机上的唯一一个端口。就算另一台主机想和我同时建立俩个TCP连接,那需要创建俩个进程或者线程,还需要俩个端口号。

下面详细讲一下 UDP 。

UDP 只是对 IP 协议进行简单的拓展。很简单的拓展。简单到做了什么事呢,就是网络层传输来的数据,区分一下端口号,就递送给进程了,如果愿意的话会选做一下错误检测。除了这些,他什么都没有做。这种简单的拓展带来了很多好处,因为简单,所以更容易的自定义,可以根据自己的需求添加一些功能,在哪自定义?在应用层。应用层可以处理 UDP 数据报,然后按照自己的想法去更改网络传输时的具体要求。UDP 除了易于在应用层对其拓展,还带来的以下的好处:

  1. 相比于 TCP 不需要建立连接,所以发送数据的延迟小,不用辛辛苦苦先建立好连接之后在发送,可以想发就发。
  2. 实现比较简单,不需要负责的实现过程。比如维护连接等。
  3. 头部开销比较小。在数据内容之上,UDP会在数据中额外添加一下数据用于区分端口号和差错检测字段,这些数据称之为 UDP 的头部。会在下文讲。
  4. 应用层可以很好的控制发送的时间和发送的数据。想要用 UDP 实现复杂的功能,给你自由,自定义去吧。

上面这些特点主要是和 TCP 做的对比。

下面我们看一下 UDP 数据报是什么样子。

大家可以看到,一个 UDP 数据报除了具体要传输的数据之外,额外添加一些数据(首部)。这些数据一共是 8 字节。所以 UDP 的首部是 8 字节。前俩个是指明数据报从本机哪个端口来,要到对方电脑的哪个端口。还描述了一下数据的长度,这个长度是包括首部8字节的,所以为了数据长度字段能正确表示数据报的长度有限,所以数据部分的长度是有限制的。其实一般情况下,数据部分都不会太大。因为具有简洁的头部,所以 UDP 就是一个词,简单。

UDP 首部中,源端口号甚至可以省略,省略了代表的含义是我这个数据只需要发出去,不需要回复,因为就算我想回复也不知道你的端口是多少。校验和字段可以省略,也就是不做数据的校验工作,但一般情况都是会校验一下的。数据校验是为了防止数据在传输过程中出现一些差错,导致数据不可用,导致残缺,或者是乱序的,假如将残缺的数据强行显示在电脑上就是乱码,无法使用。如果数据报出现错误就会在传输层废弃了,废弃后会给发松数据的主机发一个 ICMP 数据报,告诉他,数据出错了,然后数据的传输过程就结束了!不会在重传,UDP 不提供重传的机制。就算重传了也是应用层控制的,而且是应用层重新构造了一遍数据,再发了一次,UDP 不提供重传!然后,ICMP 数据报本来想在后面讲的,写不动了。。。下次讲。

继续。

在进行差错检验时,UDP 有一个比较难理解的地方。差错检验的方法这里不提,网上很多资料,与 IP 协议中差错校验方法时一样的。UDP 的差错检验做到了保证端口到端口的正确传输的检测机制。也能核实出数据部分是否产生了错误。所以 UDP 的差错检测是传输过程中唯一的一次对数据部分进行检测的过程(TCP也会检测)。而在进行差错检测时,会添加一些额外的数据参与差错检测的计算。就仅仅是用一下,计算完校验和之后就不用了,然后把这个数据递交给下一层,递交的数据格式就是上面那张图片那个样子,那个才是真正递交的内容。加入了哪些内容呢,看下图。

红色部分称为伪首部,不要被名字误导了,它只是拿过来参与运算一下,类似于加密过程中的密码,计算密文的时候需要用一下,计算完之后还放回去,不会夹在密文中一起传输过去。在这里,差错检测实现了一个很有效的功能,可以区分出来数据传输的端口是不是正确的,还能区分出来用的是不是 UDP 协议,UDP 协议在协议号字段的值为 17 (八进制为)。IPv6 协议下的红框部分与这个不相同,这样就查出在 IP 层没检测出来的错误,这个校验和保证了整个递交数据的正确性。

好了,UDP难点讲完了,关于UDP的用途就不在说了,关于伪首部这一块的内容,我发现互联网上没有很详细的资料,也没有准确的说法,最后我查看了一下 UDP 的 RFC 文档才明白的。

讲一下 TCP

TCP 是一个出色的协议,提供了很多丰富的功能,他是一个点到点的,也就是端口到端口的协议。是一个可靠的,按序字节流的协议。面向连接的协议。

接收方和发送方都会有一定存储空间缓存数据,一般用于重传,分组重组等功能。

面向连接的含义:通信前必须建立连接。 俩端来维护连接状态,中间节点不维护。

TCP 实现的功能有提供可靠的数据传输,流量控制,拥塞控制。

TCP 实现可靠数据传输的功能是基于一种确认(确认:acknowledgement,简写成 ACK)机制,也就是说,我给你发送一个数据包,你要收到了,你一定要告我一声你收到了,如果你不告我,我就当作没收到,会再给你发一次,直到你告诉我你收到了为止。就是这样的一个逻辑,保证了数据的可靠传输。

在可靠传输具体的实现过程中,TCP 采用的是累计确认,确认的是数据中的字节号,而不是确认收到了第几个数据块儿,正因为如此, TCP 被称之为按序字节流的协议。

在 TCP 数据报发出去之后,我等了半天没有等到你的回复,我就会在给你发一遍。我到底等多久呢,这就需要仔细考虑了,计算超时的时间有一个算法,里面最主要的变量就是 RTT (Round Trip delay Time) ,RTT 指的是我发出去一个报文开始,到接收到确认(ACK)报文之后经过的时间。根据历史值的平均数,和上一个数据包的 RTT 值,经过加权计算之后的值设置为当前数据报的超时时间。这样结合了当前网络状况和历史网络状况之后,计算出来的值更加合理。如果我发出去一个报文,超过了超时时间,就会重传刚刚那个数据报。另一种情况,TCP 有一个滑动窗口机制,因为发送一个字节,等待一个确认会严重影响数据的传输效率的,所以就一段一段的发,确认时,只按照收到了第几个字节数来确认。在这一个过程中,会出现一个情况,就是第五段丢失了,第六,七,八段传过去了被接收了,接收方因为没有收到第五段的内容,在收到第六段时会发送序号为第五段首字节的序号确认(ACK)报文,告知,我这个字节还没收到,然后立马收到第七段,没办法啊,在发一个序号为第五段首字节的序号确认报文,然后立马又收到第七段。只要连续发送了 3 个相同序号的确认报文,发送端会立即重发第五段报文,而不用等到计时器超时之后在重传。这种机制叫做快重传。为什么设置为 3 次重复确认报文就立刻重传呢,www.zhihu.com/question/21… 这里有清晰的回答,经过理论证明,3次最好。

TCP 还实现了流量控制的功能,如何实现流量控制的呢,刚刚提到 TCP 有一个滑动窗口的机制。接收方也有一个缓存窗口。如果应用数据读取数据很慢,网速却很快,数据大量集中,超过了接收方的的缓存大小,即便再传来数据也无法接受,那也只能不断返回最后一个进入缓存区的数据段的确认报文,超过三次,发送方会不断的重复发送相同的数据,造成网络资源的浪费。由此提出了流量控制机制,在每次返回确认报文时,会在报文中加入缓存剩余量是多少。发送方根据你接收方还能接受到少缓存来发送数据,因为他知道,即便发的多了你也接收不到。其中有一点需要注意,如果发送方收到接收方的确认报文中,发现缓存余量为 0 该怎么办?肯定不能停止发送,如果停止了就再也无法获得对方的窗口剩余数量了,即便对方已经有大量缓存的情况下,我没有给他发送数据,也就稍带不回来对方的剩余缓存。所以如果对方缓存余量为0,发送方会间接性,尝试性的发送一组探测报文( ZWP ),探测一下对方还有多少缓存,因为这个报文无论是接收,还是被扔掉,都会收到接收方的确认报文,而确认报文中含有缓存余量的信息,这样就是实现了 TCP 的流量控制原理。

TCP 实现的另一个功能是拥塞控制,TCP 采用的是端到端的控制方法,就是俩端计算机来控制,中间的路由设备不提供明显的控制。TCP 拥塞控制有多种算法。目前TCP流量控制的流程大概是这样的。最一开始,采用慢启动算法,发送窗口大小依次为1,2,4,8,16,32,64。当这个窗口到达某一阀值,开始缓慢增加,这里把阀值设置为 64。开始采用加性增算法,也就是发送窗口一个一个的加,65,66,67……,当增加到80时,突发收到三个重复的 ACK,我们认为80报文丢失了,立即乘性减(减一半),窗口降到 40。然后在加性增,一个一个增。假如增加到 50 时,发现一个数据报超时了,这时,直接降窗口大小降为 1,因为此时网络环境极差,为什么呢,我们知道,收到三次确认报文,说明对方至少收到了三个数据包,只不过数据包是乱序的,还能返回给发送方三个相同的确认报文,此时网络是较为通畅的,因为只有一个丢了,其他三个都到了,而超时则代表着发出去的包可能都没有被发送方接收,在中间路由就被丢弃了,所以此时网络环境很差,需要降低发送速率,防止拥塞。为什么会有拥塞机制呢,大家可以想一下没有这个机制的情况下,中间路由已经不堪重负了,发送方超时之后还不断的发数据,所有的主机都不停的发送数据,因为大家都超时了,都需要重传,肯定加大拥塞情况啊,就像堵车一样,明明堵住了,还不断有新的车排在后面,当然会越来越堵啊,所以才会有拥塞控制机制。然而其他协议并没有拥塞控制,比如 UDP,ICMP,UDP 丝毫不受网络限制,想发就发,网络堵住了,没事,反正我不需要保证数据必须到达。而且 UDP 的传输速率是没有限制,所以目前的网络环境中,大量的 UDP 数据报会对网络的产生严重的影响。在拥塞控制中,TCP 用到了慢启动,加性增,乘性减,快恢复的算法。有需要的可以详细了解一下,这块内容稍稍复杂,平常网络中,这些算法都会用到,不会单一的使用具体某一个算法的。

好了,TCP 的一些功能讲完了,我们看一下 TCP 的报文头。

  • 1、源端口号:数据发起者的端口号,16bit
  • 2、目的端口号:数据接收者的端口号,16bit
  • 3、序列号:32bit的序列号,真正发送数据时使用,告知当前发送的一段数据的首字节的序列号,如果发的报文不含数据(如确认报文),此字段是没有变化的。若 SYN=1 或 FIN =1 的数据报当作含有一字节数据。
  • 4、确认序列号:32bit的确认号,是接收数据方期望收到发送方的下一个报文段的序号,因此确认序号应当是对方刚刚发给你的 TCP 报文中的序号字段的值再加1,是你的确认序号,你要回复他说,我收到你的数据了,所以要确认的是对方的序号。
  • 5、首部长度:4位,最大可表示的数为 15 。而光首部就有 20 字节,所以在这里规定,每个 1 代表 4 字节,就是上图中的一行,这个字段值为几,就是几行。最大可表示 15*4 = 60 字节。
  • 6、保留:6bit,均为0
  • 7、紧急URG:当URG=1时,表示报文段中有紧急数据,应尽快传送,紧急指针字段有效。
  • 8、确认比特ACK:ACK = 1时代表这是一个确认报文,取值 0 则不是确认报文;
  • 9、推送比特PSH:当发送端PSH=1时,接收端尽快的交付给应用进程;
  • 10、复位比特(RST):当RST=1时,表明TCP连接中出现严重差错,必须释放连接,再重新建立连接;
  • 11、同步比特SYN:在建立连接是用来同步序号。一个报文中 SYN=1,ACK=0时,是表示这一个连接请求报文段。SYN=1,ACK=1时表示的是接收方同意建立连接。
  • 12、终止比特FIN:FIN=1时,表明此报文段的发送端的数据已经发送完毕,并要求释放传输连接。
  • 13、窗口:就是提供流量控制的功能,表示可缓存字节数的多少。
  • 14、校验和:该字段检验的范围包括首部和数据这两部分。由发端计算和存储,并由收端进行验证。
  • 15、紧急指针:紧急指针在URG=1时才有效,它指出本报文段中的紧急数据的字节数。
  • 16、选项:长度可变,最长可达40字节。绝大多数不适用此字段,也就是说 TCP 绝大部分是 20 字节。

需要提一下 TCP 校验时和也需要加入一个伪首部参与运算,TCP 伪首部中协议字段的值为 6,UDP 是 17,还记得吧。传输层的差错校验是整个传输过程中唯一一次对数据的正确性也进行校验的。

下面讲一下 TCP 的连接机制。

我们知道 TCP 是面向连接的。双方交换数据前需要先建立连接。TCP 的连接机制就是比较著名的 3 次握手,4 次挥手。4 次挥手指的是 TCP 断开连接的过程。下面详细讲一下。

3 次握手的过程。

在讲握手机制之前需要在巩固一些概念。序列号,用于标示我发送的数据的第一个字节的序号。在建立连接时,会先根据计算机的时钟信息初始化一个序列号,比如 5433,这是操作系统规定的,部分资料说是每 4us 就加 1 ,我暂时还没有了解那么底层。此时我只是要建立连接,并没有发送任何数据。所以我的头部信息中的序号就是初始值

1.其中某一端发起建立连接请求:向目标电脑发送一个建立连接的报文。于此同时,初始化一个序列号(sequence = seq)X ,此时 SYN=1。序列号的初始化是根据系统启动时间来确定,主要为了防止中间人伪造连接。

2.目标电脑接收到建立请求,决定同意建立请求,返回一个报文。SYN=1 ,ACK =1 。因为 SYN=1,所以目标电脑也需要初始化一个序列号 Y 。ACK=1 代表确认序列号有效。确认序列号的值为 X+1,简写成 ACK=X+1。

3.收到目标电脑的确认建立请求报文,向目标电脑发送一个确认报文。其中 ACK=1 代表时确认报文,确认序列号的置为 Y+1;

需要注意的是,确认号确认的是对方发来的数据我接收到了,所以确认号是根据对方发来的数据报对应的序列号加上数据长度得出的。

俩台电脑在建立连接时序列号是不同的。建立连接三次握手的过程就是为了沟通交换双方的序列号。

在详细说一下序列号这个东西。初始化,是由系统的时间信息初始化一个值。每发送出去一字节的数据,序列号+1 。 其中 SYN =1 时,或 FIN=1 时,序列号也 +1 。序列号相对于初始化的序列号的偏移量就是发出去的字节数再 -1 。因为建立连接时,多加了一个 1 嘛。确认号呢,就是发送过来的数据报对应的序列号加上数据长度的值。每一端都有自己的序列号。好了,讲清楚了吧。

为什么是三次握手,而不是俩次握手呢。

因为有可能出现这样的情况,我想和你建立连接,SYN=1 seq=4444 ,但是这个数据包过了很久都没有传到你那边。我以为这个数据报丢失了。于是重新发起建立连接的请求 SYN=1 ,ACK=0 ,seq=5555。但是之前seq = 4444 的报文居然到你那儿了,你回复了一个同意建立请求(SYN=1,ACK=4445)。这是俩次握手,出现了问题,我的 seq 不匹配。我要发送数据的话 seq 的值是根据 5555 来相对计算的,而不是 4444 ,双方交换数据就会出现错误了。所以必须三次握手,你收到了 ACK=4445 的确认报文,你知道这是过期了的请求,不去处理他,继续等待 ACK=5556 的确认报文,等到之后,回复一个 ACK=Y+1 的确认报文,至此,你俩交换了 seq 值,就可以开始传输数据啦。

下面是四次挥手的机制。

我准备断开连接了,发送 FIN=1 seq = 2222 ACK=1( 无论是否确认过上一个报文,这里 ACK 都要为1。官方文档中规定,除了建立连接时,ACK 为 0 以外,其他所有的报文 ACK 都为 1,包括断开链接时的报文,因为网络不保证确认报文的稳定交付,所以这里索性加上ACK,反正这个字段不用也是浪费。 )

1 我一但发出 FIN 报文,我就不会在给你发送任何有数据的报文了。

2 你收到了我的FIN 报文。回复一个确认报文 (ACK=2222+1)。

此时,你虽然应答了我的断开连接报文,但是你仍可以继续传输数据。比如你的数据还没有穿完我就发给你一个断开连接的报文,如果你没有传完数据,是可以继续一直传的。

你传一个数据,我返回一个确认报文,你传一个,我返回一个。终于传完了,你觉得可以断开连接了。

于是,3 你给我发送一个 FIN 报文。

我收到你的FIN报文,4 返回给你一个确认报文。如果你收到了确认报文就可以立马断开连接了。而我不行,我担心确认报文会在路上丢失,丢失了的话你会重传FIN报文的。如果在一段时间(2倍的数据报最大存活时间)后,没有收到你重传的FIN报文,我也就断开连接了。

四次挥手的原因是 TCP 是全双工的,就是你可以给我发数据,我也可以给你发数据。四次挥手的原因是,任何一方都需要断开连接,也就是俩次挥手的记录,A 发送 FIN,B 返回 ACK,就代表 A 已经确定了 B 收到了自己的断开连接请求了。同理 B 也一样,也需要获得断开连接的确认报文。所以一共是四次挥手。

还需要注意的是,即便 A 申请断开连接,B 仍可以继续发送数据。还需要注意的是,四次挥手可以简化成 3 次挥手,就是 A 申请端口连接,B 没有要发的数据,直接返回一个带FIN的确认报文,A 在返回一个确认报文,双方就都断开连接了。其实也是四次,只不过中间两部放在一个报文中了。

为什么要四次挥手呢?

没有那么多为什么,就得四次啊,一边发送一个 FIN 报文和确认报文,加起来四个。三个可以嘛,可以啊,上面不是说了么。我的意思是说,能不能少一个确认,当然不行了啊,少一个确认我怎么知道你收没收到我的断开连接的报文啊。嗯。就这样回答就行了。

其实更有意义的问题是,主动断开请求的一方为什么需要等待俩倍的TCP中最大报文段生存时间(MSL ),也就是2MSL(Maximum Segment Lifetime)。

等待 2MSL 时间主要目的是怕最后一个 ACK 报文对方没收到,那么对方在等待你的 ACK 报文超时后将重发第三次挥手的 FIN 包,主动关闭端接到重发的 FIN 包后可以再发一个 ACK 应答包。在 TIME_WAIT 状态时两端的端口不能使用,要等到 2MSL 时间结束才可继续使用。当连接处于 2MSL 等待阶段时任何迟到的报文段都将被丢弃。TTL 与 MSL 是有关系的但不是简单的相等的关系,MSL 要大于等于 TTL。

网络层

网络层的核心功能是 转发与路由。网络层包含很多协议,因为网络层要实现路由和转发的功能,所以在这一层使用了很多路由协议,我们要实现转发功能,就得知道转发到哪,也就是得有地址,就要用到 IP 协议。IP 协议中规定一些寻址规则,数据报过大如何切割成一小片一小片的问题。此外,网络层还包括重要的 ICMP 协议,依托于这种协议的数据都像是网络中的指令,计算机或者中间路由设备就成为了官兵的角色,根据指令内容做出相应的动作反应。

下面讲一下 IP 地址的相关内容。

我们知道 IP 地址的长度是有限的,只有 32 bit,数量也是有限的,所以需要对其进行划分,比如一部分给美国使用,一部分给中国使用。划分成大块之后,还需要继续划分,划分成更小的省市,或者企业,或者政府机关。这个划分的过程,称之为划分子网。于是就规定了, IP 地址是由两部分组成 网络号和主机号。但是网络号的位数不确定,若一个子网很大,他的网络号的位数就很少,那么主机数就多一点,子网中的主机就可以很多。举一个例子,中国联通拥有一个大的子网,所以他的网络号位数就会很少,比如有 4 位,那么主机号就剩下 28 位,那么就可以标识将近 2^28 个主机,那么属于中国联通的各个城市的网点,马路边的路由器,所有使用联通网络上网的设备,都属于联通子网内的主机。当然这样的描述极其不准确,主要是为了大家方便理解网络号和主机号的概念。

在很久之前,我们将网络地址分为五类网络地址,A,B,C,D,E类网络地址,但是对于现在来说,早已不在使用,这样划分方式造成了 IP 地址的极大浪费,所以现在几乎不用有类地址来划分子网。现在 IPv4 的 IP 地址早就已经分配完了,但是通过 NAT 技术(网络地址转换技术),还能保证每一个上网设备能有一个 IP 地址。NAT 就不讲了,有一点点复杂。。由于物联网的快速发展,网络资源的普及,上网设备越来越多,所以国家正在大力推行 IPv6 的发展。很多人这样描述 IPv6 的地址数量,可以给地球上每一粒沙子分配一个 IP 地址,更不用说设备了。滑稽一下,推进 IPv6 有利于实名制。咳咳,继续讲。

现在划分子网的方式是 CIDR (无类域间路由),形式是 x.x.x.x/n,例如 202.113.132.45/24,表示前24位网络号,那么后 8 位就是主机号了。现在划分子网的方式就是按照上述的规则来划分的。有兴趣的同学可以自己去了解子网划分的具体过程。

子网的划分根据个人兴趣去了解,下面的内容希望大家都能理解,很有用,一些特殊的网络地址:

这些特殊地址可以先做一个了解,以后在项目中会经常遇到,那时候就理解的具体的含义和概念。

下面是一些私有的网络地址,一般不用于在公网上注册,也就是说,一般访问下面的网址都是在自己的一个小局域网内访问数据:

RFC1918 规定区块名IP地址区段IP数量分类网络说明最大CIDR块 (子网掩码)主机端位长
24位区块10.0.0.0 – 10.255.255.25516,777,216单个A类网络10.0.0.0/8 (255.0.0.0)24位
Shared Address Space100.64.0.0 - 100.127.255.2554,194,30464个连续B类网络100.64.0.0/10 (255.192.0.0)22位
20位区块172.16.0.0 – 172.31.255.2551,048,57616个连续B类网络172.16.0.0/12 (255.240.0.0)20位
16位区块192.168.0.0 – 192.168.255.25565,536256个连续C类网络192.168.0.0/16 (255.255.0.0)16位

了解即可。

好了,网络方面的知识普及结束,下面看一下数据方面的格式要求。从传输层传来的 TCP or UDP 的段需要在网络层添加一个首部,然后再提交给数据链路层。网络层对数据做了什么事情呢,首先网络层是有转发功能的,转发需要知道目的地,于是网络层对数据加入了目的地址,目的地址就是 IP 地址,唯一的标示一台主机。网络层还会对超过 MTU ( Maximum Transfer Unit 最大传输单元)的数据包进行分片。以太网的 MTU 是 1500 字节,如果这个值太小,一个数据包会被分隔成很多块,如果这个值过大,传输的延迟就会增加,数据包的错误率也会增加,最终选择为 1500 字节,属于规定的一种吧。好了,如果传输层要传输的数据大于 MTU 那么这个数据在网络层就会被分片。下面看一下 IP 数据报的首部:

  1. 版本 4bit 指 IP 协议的版本。目前广泛使用的 IP 协议版本号为 4 ,IPv6,版本号为 6 。

  2. 首部长度 4bit 单位是 4 个字节( 32bit 也就是上面的一行。因为 4bit 最大描述的数值是15,首部长度最短也需要20字节,不够描述的,所以单位改成 4 字节),那这里的值一般为 5 。首部长度也就是 20 字节。首部长度大小取值范围 20 字节到 60 (4*15)字节,首部长度一般都是 20 字节。当 IP 分组的首部长度不是 4 字节的整数倍时,必须利用最后的填充字段加以填充。因此数据部分永远在4字节的整数倍开始。

  3. 区分服务 8bit 目前几乎不使用,若要支持区分服务,需要中间所有的路由器都支持区分服务,但是目前的绝大多数路由器都不支持,所以区分服务几乎不使用了。

  4. 总长度 16bit 首部长度 + 数据长度,单位是字节。数据报的最大长度为 2^16-1 = 65535 字节。但由于数据链路层的最大传送单元 MTU < 65535,所与当一个数据报封装成链路层的帧时,此数据报的总长度(即首部加上数据部分)一定不能超过下面的数据链路层的MTU值,若超过则需要分片(因此有上图的第二行,用于切片)。

  5. 标识 16bit 软件在存储器中维持一个计数器,每产生一个数据报,计数器就加1,并将此值赋给标识字段。但这个“标识”并不是序号,因为 IP 是无连接服务,数据报不存在按序接收的问题。当数据报由于长度超过网络的 MTU 而必须分片时,这个标识字段的值就被复制到所有的数据报的标识字段中。相同的标识字段的值使分片后的各数据报片最后能正确地重装成为原来的数据报。

  6. 标志 3bit 目前只有2位有意义。

    标志字段中间的一位记为 DF (Don’t Fragment),意思是“不能分片”。只有当DF=0时才允许分片,若为 1 则不分片

    标志字段中的最低位记为 MF (More Fragment)。MF=1即表示后面“还有分片”的数据报。MF=0表示这已是若干数据报片中的最后一个。 ​

  7. 片偏移 13bit 当一个较长的 IP 数据报在分片后,分片后的某一片的第一个 bit 在原分组中的相对位置。也就是说,相对传输层数据报的数据部分的起点,该片从何处截断的。片偏移以 8 个字节为偏移单位。这就表明,每个分片的长度也一定是 8 字节( 64位 )的整数倍。那为什么 ipv4 片偏移量以 8 字节位单位呢?如果直接用字节为单位,总长度是 16bit 而片偏移量只有 13 bit,就会出现不够描述的。 所以偏移量就按 8 字节为单位的来规定了。

  8. 生存时间 8bit TTL( Time To Live ) 就是说 IP 数据报最大能经过多少个路由器跳转(每经过一个路由器,TTL减1)。防止数据报在网络中因为某些错误导致无限循环,浪费网络资源。那么可以知道,路由器在转发时会将这个字段的值减 1,并重新计算首部校验和。

  9. 协议 8bit 标志此数据报携带的数据是使用协议类型( 例如TCP、UDP等 ),以便使目的主机的 IP 层知道应将数据部分上交给哪个处理过程。

  10. 首部检验和 16bit 注意这个字段只检验数据报的首部,不包括数据部分。这是因为数据报每经过一个路由器,路由器都要重新计算一下首部检验和(因为生存时间、标志、片偏移都可能发生变化,就是说在中间路由被重新分片时会出现变化。),不检验数据部分可减少计算量。

  11. 源IP地址 占32位。

  12. 目的IP地址 占32位。

上述就是 IP 数据报的头部。

下面讲一下 DHCP(Dynamic Host Configuration Protocol)。

我们知道,设置自己电脑 IP 地址有俩种方式,第一种是直接设置一个固定的 IP 地址,同时需要配置一系列复杂的信息,比如子网掩码,DNS 服务器地址等等。这很麻烦,而且需要具备相关的专业知识。自己以前就是看着教程配置,什么都不懂,最后设置的半天也连不上网,索性直接选择自动获取 IP 地址了。

自动获取 IP 地址使用的就是 DHCP(动态主机配置协议)。通过向本网络中的 DHCP 服务器发出请求,DHCP 服务器看看自己还有那些 IP 地址没有分配出去,然后随便挑一个给你。你就有了 IP 地址。DHCP 服务器不仅仅给你提供 IP 地址,在分配 IP 地址时,连带着子网掩码,默认网关地址,DNS服务器地址都装在一个数据包中发给你了。

动态获取 IP地址有什么好处呢,第一就是简单,插上网线就能用,不用用户自己去配置。第二个就是 IP 地址可以重复使用,你的电脑关机了,我就把这个 IP 地址给别人用。

下面讲一下如何和 DHCP 服务器沟通索要 IP 地址。

  1. 想上网的设备先向本网络中广播一个 DHCP 报文,看谁回应,谁就是DHCP服务器。这个过程称之为 发送发现报文
  2. DHCP服务器在自己身体里找一个合适的 IP 地址,然后向全体广播出一个提供报文
  3. 向上网的设备会再发送一个请求报文,去广播询问能不能用某个IP地址。
  4. DHCP 服务器发送确认报文,来响应这个 IP 到底能不能用。

为什么这么麻烦呢,主要的原因是一个网络中可能有多个 DHCP 服务器,当有设备请求 IP 地址时,所有的 DHCP 都收到广播,都会给他发了一个自认为合适的 IP 地址的。上网设备收到很多 IP 地址,会随便挑一个 IP 地址,再广播一遍,这个 IP 地址能不能用,刚刚提供 IP 地址所有 DHCP 都确认一下这个 IP 是不是自己的,如果是自己的就回复一个确认报文,然后更新一下自己的数据库,这个 IP 地址已经有人用了。如果不是自己的 IP 地址就返回一个错误,收回自己刚刚分配出去的 IP 地址。

具体的通信过程如下:

  1. DHCP服务器的默认端口是 67
  2. 联网设备发送 DHCP 报文的默认端口是 68
  3. 发现报文:源 IP 0.0.0.0:68 目的 IP 255.255.255.255:67 双方都不知道彼此的 IP 地址 所以都是广播通信
  4. 提供报文:源 IP x.x.x.x:67 目的IP 255.255.255.255:67
  5. 下面就是再发一个请求报文和确认报文了。

确认报文中包含有 IP 地址,子网掩码等信息,还有一些数据,比如 IP 地址的过期时间,方便回收 IP 地址。因为不可能分配出去不回收吧,当 IP 地址过期前,上网设备会再使用 DHCP 来续租 IP 地址。

DHCP 报文有如下特点:

  1. 在应用层实现,通过应用进程来实现
  2. 使用 UDP
  3. 使用了 IP 广播

看起来这个协议简单又有趣。

在看一个有趣的 ICMP(Internet Control Message Protocol)协议。

ICMP是互联网控制报文协议。

与 IP 息息相关。它的主要功能有差错报告和网络探测俩种。差错报告就是数据在网络中进行转发时,可能会出现一些错误,比如,数据报经过路由器时发现 TTL=0,那么路由器就会丢弃该报文,然后向发送端发送一个 ICMP 报文。而网络探测的功能,最常用的就是 ping 工具呢,这个 ping 工具基于的就是 ICMP 的回声请求应答报文,属于 ICMP 中的一种报文格式。根据 ICMP 的功能,ICMP 报文也分为了两大类。

差错报文:

​ 目的不可达报文: 比如目的网络不可达,目的端口不可达等问题,会发送这类报文。

​ 源抑制:就是当路由器缓存满了之后,路由器会向源主机发送抑制发送速度的报文,来进行拥塞控制,但是目前使用的是别的拥塞控制方法,这种报文就很少用了。

​ 超时/超期报文:最典型的中间路由设备的数量超过了 TTL 的值。

​ 参数错误:如数据报头部有问题时,会发送这类报文。

​ 重定向:当目的网络不是我这个路由器转发的地址,而是我旁边另一个路由器转发的地址,就会给源主机发送一个这样的报文。

第二类是网络探寻的报文:

​ 回声请求与应答报文:ping

​ 时间戳请求与应答报文:用于获取时间戳。

其实 ICMP 的报文类型挺多的,也不容易理解,大家浅尝辄止就好,不用过于深究,只要记住网络上传输过程中很多的报告类信息,一般都基于 ICMP 就好。

下面有一些不发送 ICMP 报文的情况:

  • 如果发送的本来就是 ICMP 报文,而数据报出现了错误,此时不会在发送了,如果发送的话容易死循环。
  • IP数据报分片只对第一个分片发送 ICMP 报文,其他的分片不发。比如出现目标网络不可达的错误,第一个问题,那后续所有分组的报文都是往同一个网络发送数据,为了减少重复,减少网络的消耗,一般只对第一个分片发送。
  • 使用多播 IP 地址的数据报不发送 ICMP报文,因为这是面向所有主机发送的广播,容易引起大范围的错误报文,所以规定这种类型的数据报不发送 ICMP 报文。
  • 特殊 IP 地址的数据报不发送 如 127.0.0.0 或 0.0.0.0;

目前随着互联网的发展,还有几种 ICMP 报文已经不在使用了,比如:

​ 信息请求于应答报文。

​ 子网掩码请求和应答报文。

​ 路由器询问和通告报文

下面看一下 ICMP 的神奇用法 Tracerout,用来探测一个数据报发送到目的主机所经过的路由器信息。

traceroute 探测路由路径

第一组 IP 数据报将 TTL 设置为 1 ,那么到达下一个路由肯定会被丢弃,路由器会发一个 ICMP 报文,然后来测量往返时间

第二组 IP 数据报 TTL=2

...

….

…….

停止条件:traceroute 在封装 UDP 报文时使用的是一个几乎不会用的端口号,到目的主机时,目的主机会返回一个端口不可达的错误报文。。这样就探测了完整的路由路径。测量路由时,一组会发三个数据报,防止出现问题。

好了,网络层的相关知识也讲完了,不知道大家看的过瘾不过瘾,可以进行温习,反复的看,有什么问题的朋友可以和我多多交流。微信 WytheHard。


欢迎关注公众号: 吴先生的伊甸园