计算机网络网络层——学习笔记

目录

网络层绪论

虚电路网络

数据报网络

路由器

路由器的组成

输入端口

交换结构

输出端口

路由选择算法

链路状态路由算法

距离向量路由算法

层次路由选择

因特网中的路由选择

路由选择信息协议(RIP协议)

开放短路优先(OSPF)

边界网关协议(BGP)

广播与多播路由选择

广播路由选择算法

多播路由选择算法

网际数据报

IPv4编址

动态主机配置协议(DHCP)

因特网控制报文协议(ICMP)

编程作业

一、ICMP ping


网络层绪论

数据从传输层往下是网络层,网络层下接数据链路层;网络层主要功能是:实现两个不同网络系统之间的数据传输,也就是我们说的主机到主机的通信,这就要区别于传输层的“端到端的通信”。网络层负责在不同的网络之间(基于数据包的IP地址)尽最大能力传输数据,它不负责丢包重传、传输失序等问题,因为那是传输层的任务。

我们可能需要网络层提供各种服务,比如:

确保交付 确保分组最终到达其目的地
具有时延上界确保交付 除了确保交付,还有时延界限
有序分组交付 确保分组以它们发送的顺序到达目的地
确保最小带宽         在发送和接收双发使用一条低于一定速度的传输链路,当发送方低于该速度时,分组不会丢失
确保最大时延抖动 确保发送方相继的两个发送分组之间的时间量等于接收方接收到它们的时间量,或抖动不超过某一个值
安全性服务 可以使用只有通信双方知道的密钥通信,发送方加密内容,接收方解密。

但实际上因特网的网络层只有一个服务:尽力而为服务,也就是什么都不干,就是来数据了,尽力传出去,其它就不管了。

除了因特网,有更负责的网络体系的网络,比如ATM网络,会保证传输顺序、提供拥塞指示等。这些网络结构体系是需要连接的,这就有虚电路网络。因特网是数据报网络。

虚电路网络

虚电路服务,它不依靠路由技术而是在进行数据分组转发前先在源节点和目的节点间的所有路由器间建立一条虚拟的通信通道,然后再把数据分组从这个虚拟通道中转发到目的节点。
采用虚电路这种服务方式的分组则无须添加源和目的主机地址信息,只需要添加报文号、分组号信息和虚电路编号。在分组发送之前,已经搭建好了一条逻辑链路。

虚电路网络组成:

  1. 源和目的主机之间的路径(即一系列链路和路由器)
  2. VC号,沿着该路径的每段链路的一个号码
  3. 沿着该路径的每台路由器中的转发表表项。

计算机网络网络层——学习笔记_第1张图片

 通过上图可以举例:当分组从A到R1到R2到B时,它的VC可以从12到22到32,每台路由器都有一个转发表,如下图:

计算机网络网络层——学习笔记_第2张图片

 每台路由器中,如果创建一条虚电路,则增加一条VC号,删除一条虚电路则减少一条VC号。

 虚电路过程:

  • 虚电路建立。在建立阶段,发送传输层与网络层联系,指定收发方地址,等待网络建立虚电路,然后网络层决定发送方与接收方之间的路径,网络层也为沿着该路径的每条链路决定一个VC号,网络层还可以预留该虚电路路径上的资源。
  • 数据传送。一旦建立了虚电路,分组就开始沿该虚电路流动。
  • 虚电路拆除。当发送方(或接收方)通知网络层它希望终止该虚电路时,就启动这个阶段。然后网络层通知网络另一侧的端系统结束呼叫,并更新路径上每台路由器的转发表以表明该虚电路不存在了。

数据报网络

当每一个端系统要发送分组,它就为该分组加上目的端系统的地址,然后打到网络中。

当端系统要发送数据时,会为每个分组添加报文号、分组号、目的地址、源地址和校验字段信息,然后作为数据报发送给网络节点(路由器),由通信子网中的节点进行路由选择,当一个报文的所有分组到达了目的主机后,再将各个分组按序号编排起来;
网络在发送分组时不需要先建立连接。每一个分组(即 IP 数据报)独立发送,与其前后的分组无关(不进行编号)。
网络层不提供服务质量的承诺。即所传送的分组可能出错、丢失、重复和失序(不按序到达终点),当然也不保证分组传送的时限。
目的端系统收到数据报可能是不按照顺序到达,也有可能出现数据报丢失。网络层于此只需尽最大努力持续交付即可。数据报服务与OSI的无连接网络服务类似。

尽最大努力交付的好处:

  • 由于传输网络层不提供端到端的可靠传输服务,这就使网络中的路由器可以做得比较简单,而且价格低廉(与电信网的交换机相比较)。
  • 如果主机(即端系统)中的进程之间的通信需要是可靠的,那么就由网络的主机中的传输层负责(包括差错处理、流量控制等)。
  • 采用这种设计思路的好处是:网络的造价大大降低,运行方式灵活,能够适应多种应用。

路由器

路由器是网络层使用的中间设备,它既配有IP地址, 又能进行路由控制。路由器中运行的有网络层、数据链路层和物理层。直观来说,路由器的功能就是为经过路由器的数据寻找一条最佳传输路线和把数据传到目的地。

计算机网络网络层——学习笔记_第3张图片

选择通畅快捷的近路,能大大提高通信速度,减轻网络系统通信负荷,节省网络系统资源,提高网络系统畅通率。由此可见选择最佳路径的策略即路由算法是路由器的关键所在。通常情况下,路由器根据接收到的IP数据包的目的网段地址查找路由表决定转发路径。路由表中需要保存子网的标志信息、网上路由器的个数和要到达此目的网段需要将IP数据包转发至哪一个下一跳相邻设备地址等内容,以供路由器查询使用。
路由表被存放在路由器的RAM上,这意味着路由器如果要维护的路由信息较多时,必须要有足够的RAM,而且一旦路由器重新启动,那么原来的路由信息都会消失。
路由器的另外一个作用是连通不同网络。一般来说,异种网络互联与多个子网互联都应采用路由器来完成。

路由器的组成

计算机网络网络层——学习笔记_第4张图片

输入端口:输入输出执行几项关键功能,它要执行将一条输入的物理链路与路由器相连接的物理层功能。更重要的是,在输入端口还要完成查找功能,通过查询转发表决定路由器的输出端口。

交换结构:交换结构将路由器的输入端口和输出端口相连。

输出端口:输出端口通常是与该链路的输入端口在同一线线路卡上成对出现。

路由选择处理器:路由选择处理器执行路由选择协议,维护路由选择表以及连接的链路状态信息,并为路由器计算转发表。

 输入端口

计算机网络网络层——学习笔记_第5张图片

线路端接:线路端接功能与链路层处理实现了用于各个输入链路的物理层和链路层,也是在这里路由器使用转发表来查询输出端口。

数据链路处理:转发表在此处计算和更新。转发表的一份副本通常会被存放在每个输入端口,有了这份副本,转发决策能在每个输入端口本地做出,无需调用中央路由选择处理器,避免了集中式处理的拥塞。

交换结构

计算机网络网络层——学习笔记_第6张图片

 有三种交换技术。

  • 经内存交换:在早期技术中,输入与输出端口的功能就像在传统操作系统中的I/O设备一样,一个分组到达一个输入端口时,该端口会先通过中断方式向路由选择处理器发出信号。然后该分组被复制到处理器内存中,路由选择处理器则从其首部提取目的地址,在转发表中找出适当的输出端口,并将该分组又复制到输出端口缓存中。
  • 经总线交换:输出端口经一条总线将分组直接传送到输出端口,不需要路由选择处理器的干预。首先让输入端口为分组预先计划一个交换机内部标签,指示本地输出端口,使分组在总线上传送和传输到输出端口,这样会使所有输出端口都能收到该分组,但只有内部标签跟输出端口匹配的才会保存下来,其它端口都将该分组销毁。这种技术当多个分组到达时,除了在处理的一个分组,其它的分组都要等待处理,因为总线一次只能传输一个分组。
  • 经互联网络交换:由2N条总线组成互联网络,它连接N个输入端口与N个输出端口,每条垂直的总线在交叉点与每条水平的总线交叉,交叉点通过交换结构控制器能在任何时候打开和闭合。纵横式与上述两种技术不同的是,它可以同时也就是并行转发多个分组,只要它们走的总线不冲突,如过冲突了还是需要等待的。

输出端口

计算机网络网络层——学习笔记_第7张图片

 输出端口取出存放在输出端口内存的分组并将其发送到输出链路上。

路由选择算法

路由选择:当分组从发送方到接收方时,网络层要决定这些分组所采用的路由或者路径。计算这些路径的算法叫路由选择算法。

路由表:每台路由器都具有一张转发表,用于保存各种传输路径数据。路由表可以是由系统管理员固定设置好的(静态路由表),也可以是根据网络系统的运行情况而自动调整的路由表(动态路由表),它是根据路由选择协议提供的功能,自动学习和记忆网络运行情况,在需要时自动计算数据传输的最佳路径。

第一跳路由器:主机通常直接与一台路由器相连接,该路由为主机的默认路由器,也叫第一跳路由器。收发信息时,把发送方的默认路由器叫源路由器,接受方的默认路由器叫目的路由器。

路由选择算法则是源路由器到目的路由器中间选择走的链路。

根据算法是全局式还是分散式区分:

  • 全局式路由选择算法 (global routing algorithm):所有的路由器掌握完整的网络拓扑和链路费用信息实践中,具有全局状态信息的算法常被称作链路状态 (Link State , LS) 算法,因为该算法必须知道网络中每条链路的费用
  • 分散式路由选择算法 (decentralized routing algorithm):路由器只掌握物理相连的邻居及链路费用以迭代、分布式的方式计算出最低费用路径例如,距离向量( Distance- Vector, DV) 算法

跟据算法是静态还是动态区分:

  • 静态路由选择算法 (Static routing algorithm):人工干预进行调整(如人为手工编辑一台路由器的转发表)
  • 动态路由选择算法 (Dynamic routing algorithm):能够当网络流量负载或拓扑发生变化时动态改变路由选择路径

根据算法负载敏感还是负载迟钝区分:

  • 负载敏感算法 ( load-sensitive algorithm):链路费用会动态地变化以反映出底层链路的当前拥塞水平;早期的 ARPAnet 路由选择算法就是负载敏感的
  • 当今的因特网路由选择算法都是负载迟钝的 (load-insensitive) ,因为某条链路的费用不明思地反映其当前(或最近)的拥塞水平

链路状态路由算法

在链路状态算法中,网络拓扑和所有的链路费用都是已知的,也就是说可用作 LS 算法的输入
实践中这是通过让每个结点向网络中所有其他结点广播链路状态分组来完成的,其中每个链路状态分组包含它所连接的链路的特征和费用
链路状态路由选择算法叫做 Dijkstra 算法(数据结构与算法,具体略)
复杂性:n 个节点需要 n(n+1)/2 次比较,最差的复杂度即O(n^2)
存在震荡可能

距离向量路由算法

距离向量算法是一种迭代的、异步的和分布式的算法
分布式:每个结点都要从一个或多个直接相连邻居接收某些信息,执行计算,将其计算结果分发给邻居
迭代:此过程一直要持续到邻居之间无更多信息要交换为止
异步:不要求所有结点相互之间步伐一致地操作。
采用Bellman-Ford 算法(算法数据结构与算法,具体略)
无穷计数问题:采用毒性逆转技术或定义最大度量

LS算法是一种全局算法,它要求每个节点在运行Dijkstra算法之前,首先获得该网络的完整信息。DV算法是分布式的,但实际中节点的信息只有与它直接相连的节点的链路费用和从邻居中得到的消息。

DV 和 LS 算法采用互补的方法来解决路由选择计算问题
在 DV 算法中,每个结点仅与它的直接相连的邻居交谈,它为其邻居提供了从它自己到网络中(它所知道的)所有其他结点的最低费用估计
在 LS 算法中,每个结点(经广播)与所有其他结点交谈,它仅告诉它们与它直接相连链路的费用。
 

层次路由选择

将任意规模的网络抽象成图计算路由——过于理想化和简单化,原因如下:

  • 规模。随着路由器数目变大,涉及路由选择信息的计算、存储及通信的开销将高得不可实现。
  • 管理自治。每个网络的管理可能都期望自主控制其内网的路由,还要能将其网络与其他外部网络相连接。

这两个问题都可以通过将路由器组织进自治系统 (Autonomous System, AS) 来解决,

  • 在相同的 AS 中的路由器都全部运行同样的路由选择算法,且拥有彼此的信息,就像理想化那样。
  • 在一个自治系统内运行的路由选择算法叫做自治系统内部路由选择协议( intra- autonomous system routing protocol)
  • 不同自治系统内的路由器可以运行不同的AS内部路由协议
  • 将 AS 彼此互联是必需的,因此在一个 AS 内的某些路由器还将负责向在本 AS 之外的目的地转发分组,这些路由器位于AS的 “边缘”,被称为网关路由器 (gateway router) 。

因特网中的路由选择

路由选择信息协议(RIP协议)

在自治系统中,内部网管协议协议(RIP或OSPF)去通信,而在区域之间进行通信会使用外部外部网关协议(BGP)去通信。

计算机网络网络层——学习笔记_第8张图片计算机网络网络层——学习笔记_第9张图片

RIP是最早的AS内部因特网路由选择协议之一,它使用跳数作为费用测度,即每一跳费用是1,RIP一条路径最大费用是15,即不能超过15跳。路由器维护的路由表,记录着主机A到每一个节点的跳数。RIP中,路由选择更新更新信息在邻居之间通过使用一种RIP响应报文来交换,大约每30s互相交换一次。如果一台路由器超过180秒没有从邻居听到报文,则该邻居会被认为死机了,并且会向大家传播该消息,然后更新路由表。有趣的是,RIP是在UDP协议上使用520端口互相发送RIP请求和响应报文。

开放短路优先(OSPF)

OSPF的核心是一个使用泛洪链路状态信息的链路状态协议和一个Dijkstra最低费用路径算法。使用OSPF的路由器构建了一副关于整个自治系统的完整拓扑图,它可以在本地就运行最短路径算法。

使用OSPF时,路由器会向整个自治系统的路由器广播其路径选择信息,当每条链路发发生状态变化时,它就会广播,即使没发生变化也会定时广播。

优点:

  • 安全:能够鉴别OSPF路由器之间的交换,使用鉴别,让只有受信任的路由器能参与AS内部的OSPF协议,一次可以防止恶意入侵。
  • 多条相同费用的路径:当到达某目的地的多条路径具有相同费用时,OSPF允许使用多条路径,就是不用非要选一条路径造成拥塞。
  • 对单播与多播路由选择的综合支持:多播OSPF最重要的优点是具有按层次结构构造一个自治系统的能力。

边界网关协议(BGP)

当两个AS需要交换路由信息时,每个AS都必须指定一个运行BGP的节点,来代表AS与其他的AS交换路由信息。两个AS中利用BGP交换信息的路由器也被称为边界网关(Border Gateway)或边界路由器(Border Router)。

广播与多播路由选择

广播路由选择算法

广播是指在IP子网内广播数据包,所有在子网内部的主机都将受到这些数据包。广播意味着网络向子网每一个主机都投递一份数据包,不论这些主机是否乐于接收该数据包。所以广播的使用范围非常小,只在本地子网内有效,通过路由器和交换机网络设备控制广播传输。

最简单的的发送方式:向每个目的地发送一个副本,N次单播,但效率低,每个发送的副本都将通过开头那段链路传输,每个目的地的地址都必须被发送方知晓,不太可能实现。

更有效的方式:源节点仅向第一跳发送副本,然后由第一跳向第二跳发送副本,以此类推。

无控制洪泛:实现广播的最显而易见的技术,源节点向所有邻居发送副本,邻居接收到后向除了源节点外的所有邻居转发副本。

受控洪泛: 解决广播风暴的方法,避免广播风暴的关键是每个结点明智地选择何时洪泛分组,何时不洪泛分组。

解决:

  • 序号控制洪泛:源结点将其地址(或其他唯一的标识符)以及广播序号 (broadcast sequence number) 放入广播分组,再向它的所有邻居发送该分组 。每个结点维护它已经收到的、复制的和转发的源地址和每个广播分组的序号列表 。当结点接收到一个广播分组时,它首先检查该分组是否在列表中 。 如果在,丢弃该分组;如果不在,复制该分组并向该结点的所有邻居转发(除了接收到该分组从其的那个结点) 。
  • 反向路径转发 :当一台路由器收到具有给定源地址的广播分组时,仅当该分组到达的链路正好是位于它自己的返回其源的最短单播路径上,才向其他出链路转发报文,否则丢弃。注意到 RPF 不使用单播路由选择以实际将分组交付给目的地,它也不要求路由器知道从它自己到源的完整最短路径 。仅需要知道在它到发送方的单播最短路径上的下一个邻居;它仅使用这个邻居的身份以决定是否洪泛一个接收到的广播分组。
  • 生成树广播: 冗余广播分组的消除;首先对网络结点构造出一棵生成树。当一个源结点要发送一个广播分组时,它向所有属于该生成树的特定链路发送分组 。
    接收广播分组的结点则向在生成树中的所有邻居转发该分组(其接收该分组的邻居除外)。
    生成树不仅消除了冗余的广播分组,而且一旦合适,该生成树能够被任何结点用于开始广播分组。注意到一个结点不必知道整棵树;只需要知道它在 G 中的哪些邻居是生成树的邻居 。
     

多播路由选择算法

也叫组播,组播在发送者和每一接收者之间实现点对点网络连接。如果一台发送者同时给多个的接收者传输相同的数据,也只需复制一份的相同数据包。它提高了数据传送效率,减少了骨干网络出现拥塞的可能性。

在因特网体系结构中,多播数据报使用间接地址来编址,用一个标识来表示一组接收方,寻址到该组的分组副本被交付给所有与该组相关联的多播接收方,且该组使用这个单一标识符 。
这种表示一组接收方的单一标识就是一个 D 类多播地址 。与一个 D 类地址相关联的接收方小组被称为一个多播组 (multicast group)

计算机网络网络层——学习笔记_第10张图片

多播路由选择:多播路由选择的目标就是发现一棵链路的树,这些链路连接了所有具有属于该多播组的相连主机的路由器 。于是多播分组将能够沿着这棵树从发送方路由到所有属于该多播树的主机 。距离向量多播路由选择协议实现了具有反向路径转发与剪枝算法的基于源的树,使用具有剪枝的RFP算法。

剪枝( pruning):一 台接收到多播分组的多播路由器,如它无加人该组的相连主机,则它向其上游路由器发送一个 图例,如果一台路由器从它每个下游路由器收到剪枝报文,则它就能向上游转发一个剪枝报文。

使用最为广泛的因特网多播路由选择协议是协议无关的多播路由选择协议,该协议明确辨识两种多播分发情形。

稀疏模式:源特定多播中,仅允许单一发送方向多播树中发送流量,大大简化树的构造和维护 。
稠密模式:在稠密模式中,多播组的成员位置分布稠密;这就是说,在该区域内的许多或大多数路由器需要参与到多播数据报路由选择过程之中。
 

网际数据报

网络层的分组被叫做数据报,下面是IPv4的数据报格式。

  • 版本:指定IP数据报中使用的IP协议版本,占4位。通过查看版本号,路由器能够确定如何解释IP数据报的剩余部分,IPv4对应值为4(0100)
  • 首部长度:指示IP数据报头部的总长度,占4位。IP数据报头部的总长度以4字节为单位(即4字节的整数倍)
  • 服务类型:用于表示数据报的优先级和服务类型,例如一些特别要求低时延、高吞吐量或可靠性的数据报区分开,占8位。包括一个3位长度的优先级,4位长度的标志位,最高位未用
  • 总长度:标识整个IP数据报的总长度,包括报头和数据部分,占16位,由此可知IPv4的最大长度为65535(64KB),但一般不超过1500比特。
  • 标识:用于表示IP数据报的标识符,占16位,每个IP数据报有一个唯一的标识(不是序号)。当数据报分段时,这个标识的值就被复制到所有分段的标识字段中,相同的标识字段值使分段后的数据报分段最后能正确地重组成为原来的数据报。
  • 标志:指出该IP数据报后面是否还有分段,为分段标志,占3位,仅最低位有意义
  • 片偏移:指出该分段在数据报中的相对位置。相对于用户数据字段的起点,该字段从何处开始,占13位
  • 寿命(TTL):标识IP数据报在网络中传输的有效期,以秒来计数,占8位。现在通常认为这个数值是指数据报允许经过的路由器数,也就是每当经过一个路由器后,该值-1,当值为0时,就丢弃这个数据报。设定生存时间是为了防止数据报在网络中无限制地循环转发。
  •  协议:用来标识此IP数据报在传输层所采用的协议类型(如TCP、UDP或ICMP等),以便使目的主机的IP层知道应将数据部分上交给哪个处理过程,占8位
  • 首部校验和:用来检验IP数据报的包头部分(不含“数据”部分)在传输到接收端后是否发生了变化,占16位。将首部中的每两个字节当作一个数,用反码运算对这些数求和,该和的反码存放在检验和字段中。数据报每经过一个路由器,路由器都要重新计算一下报头校验和。这要区分一下传输层的检验和,传输层是对整个报文段都检测,网络层只检验首部。
  • 源地址/目的地址:分别表示该IP数据报发送者和接收者的IP地址,各占32位
  • 选项:用来支持各种选项,提供扩展余地,可用来支持排错、测量以及安全等措施。后面的填充字段就是为了保证IP数据报的报头是32位的整数倍。

IPv4编址

每个IP地址长度为32比特(4字节),一台主机通常只有一条链路连接到网络,主机与物理链路的边界叫接口。一台路由器与它任意一条链路的边界也叫接口,则路由器有多个接口。这些接口,都必须拥有全球唯一的IP地址。一个接口的IP地址的一部分由它所连接的子网决定。

 IP地址的网络部分(net-id)长度被限制为8、16、24个比特,对应A、B、C类地址,即一个C类地址的主机ID只有8位,最多支持256-2=254台(预留两个地址用于特殊用途),这对于当今网络接入数量来说无疑太小。

私有IP地址 :在局域网中使用的IP;公网IP地址 :在互联网中使用的地址。

如果一个组织内部组建局域网,IP 地址只用于局域网内的通信,而不直接连到互联网上, 理论上使用任意的 IP 地址都可以,但是 RFC 1918 规定了用于组建局域网的私有IP地址 :

● 10. * :前8位是网络号,共16777216个地址, 用于组建大型局域网;
● 172.16. * 到 172.31. * :前12位是网络号,共1048576个地址, 用于组建中型局域网;
● 192.168.*:前16位是网络号,共65536个地址, 用于组建小型局域网;
包含以上范围中的, 都成为私有IP, 其余的则称为全局IP(或公网IP)
 

动态主机配置协议(DHCP)

一个客户端在使用因特网接入网和无限局域网时,它们会频繁进出网络,这时候如果每次固定一个IP地址,离开后依旧占用,如果不固定那它下一次接入又需要一个新的IP地址分配,都会造成资源消耗。DHCP服务器则是将局域网内的地址分块,计算同时在线的客户数量选择分块大小。当用户接入时,在地址池中随意分配可用地址,当客户离开时,该地址被收回地址池。

计算机网络网络层——学习笔记_第11张图片

交互过程:

  • DHCP服务器发现:一台新到的主机首先要发现有一个与其交互的DHCP服务器,这通过DHCP发现报文来完成。DHCP客户将DHCP发现报文的IP数据报使用广播目的地址255.255.255.255,本主机源地址0.0.0.0.然后将数据报传给链路层广播到子网。
  • DHCP服务器提供:DHCP服务器收到一个发现报文时,用DHCP提供报文向客户做出响应。
  • DHCP请求:新到达的客户从一个或多个服务器提供中选择一个,并向选中的服务器提供一个DHCP请求报文进行响应。
  • DHCP ACK:服务器用DHCP ACK报文对DHCP请求报文进行响应,证实所求参数。

因特网控制报文协议(ICMP)

ICMP用于主机和路由器彼此沟通网络层信息,最典型的用途是差错报告。如先前抓包时:目的端口不可达。

计算机网络网络层——学习笔记_第12张图片

ICMP 报文是装在 IP 数据报中,作为其中的数据部分,加上数据报的首部,组成 IP 数据报发送出去。ICMP通常被认为是IP的一部分,但从体系上讲它是位于IP之上的。

ICMP的报文结构:

ICMP主要功能:1、确认IP包是否成功到达目标地址;2、通知发送过程中IP数据包被丢弃的原因。

计算机网络网络层——学习笔记_第13张图片

ICMP 报文的种类有两种,即 ICMP 差错报告报文和 ICMP 询问报文。
ICMP 报文的前 4 个字节是统一的格式,共有三个字段:即类型、代码和检验和。接着的 4 个字节的内容与 ICMP 的类型有关

 把收到的需要进行差错报告的 IP 数据报的首部和数据字段的前 8 个字节提取出来,作为 ICMP 报文的数据字段,再加上相应的 ICMP差错报告的前8个字节,就构成了ICMP差错报告报文。
ICMP报文包含在IP数据报中,IP报头在ICMP报文的最前面。一个ICMP报文包括IP报头(至少20字节)、ICMP报头(至少八字节)和ICMP报文(属于ICMP报文的数据部分)。当IP报头中的协议字段值为1时,就说明这是一个ICMP报文。

不应发送 ICMP 差错报告报文的几种情况

  • 对 ICMP 差错报告报文不再发送 ICMP 差错报告报文。
  • 对第一个分片的数据报片的所有后续数据报片都不发送 ICMP 差错报告报文。
  • 对具有多播地址的数据报都不发送 ICMP 差错报告报文。
  • 对具有特殊地址(如127.0.0.0 或 0.0.0.0)的数据报不发送 ICMP 差错报告报文。

编程作业

一、ICMP ping

ping是一种流行的网络应用程序,用于测试位于远程某个特定主机是否开机和可达。它也经常用于测量客户主机和目标主机的时延。它的工作过程是:向目标主机发送ICMP“回显请求”分组(即ping分组),并侦听ICMP“回显响应”应答(即pong分组)。ping测量RTT、记录分组丢失和计算多个ping-pong交换(往返时间最小、平均、最大和标准差)的统计汇总。

代码及注释


# 对代码进行了少部分改动

import socket
import os
import sys
import struct
import time
import select
import binascii

ICMP_ECHO_REQUEST = 8


def checksum(strCheck):
    csum = 0                                                     #检验和函数
    countTo = (len(strCheck) / 2) * 2
    count = 0                   #对16位的二进制数进行1的补码和one'scomplementsum运算
    while count < countTo:                                          
        thisVal = strCheck[count + 1] * 256 + strCheck[count]   
        csum = csum + thisVal
        csum = csum & 0xffffffff
        count = count + 2
 #累加结果再取反码,即产生检验和
    if countTo < len(strCheck):
        csum = csum + strCheck[len(strCheck) - 1]
        csum = csum & 0xffffffff

    csum = (csum >> 16) + (csum & 0xffff)
    csum = csum + (csum >> 16)
    answer = ~csum
    answer = answer & 0xffff
    answer = answer >> 8 | (answer << 8 & 0xff00)
    return answer       #如果结果为0,则表示传输正确

#接收ping响应函数
def receiveOnePing(mySocket, ID, timeout, destAddr):                
    timeLeft = timeout

    while 1:
        startedSelect = time.time()
        whatReady = select.select([mySocket], [], [], timeLeft)
        howLongInSelect = (time.time() - startedSelect)
        if whatReady[0] == []:  # Timeout
            return "Request timed out."

        timeReceived = time.time()
        recPacket, addr = mySocket.recvfrom(1024)

        header = recPacket[20:28]
        header_type, header_code, header_checksum, header_packet_ID, header_sequence = struct.unpack("bbHHh", header)

        if(header_type != 0 or header_code != 0 or header_packet_ID != ID or header_sequence != 1):
            return "Receive error."

        timeLeft = timeLeft - howLongInSelect
        if timeLeft <= 0:
            return "Request timed out."
        return 1 - timeLeft


def sendOnePing(mySocket, destAddr, ID):
    # Header is type (8), code (8), checksum (16), id (16), sequence (16)

    myChecksum = 0
    # Make a dummy header with a 0 checksum.
    # 创建一个带有0校验和的伪头。
    # struct -- Interpret strings as packed binary data
    # struct-将字符串解释为打包的二进制数据
    header = struct.pack("bbHHh", ICMP_ECHO_REQUEST, 0, myChecksum, ID, 1)
    data = struct.pack("d", time.time())
    # Calculate the checksum on the data and the dummy header.
    # 计算数据和虚拟头的校验和。
    myChecksum = checksum(header + data)

    # Get the right checksum, and put in the header
    if sys.platform == 'darwin':
        myChecksum = socket.htons(myChecksum) & 0xffff
        # Convert 16-bit integers from host to network byte order.
        # 将主机的16位整数转换为网络字节顺序。
    else:
        myChecksum = socket.htons(myChecksum)

    header = struct.pack("bbHHh", ICMP_ECHO_REQUEST, 0, myChecksum, ID, 1)
    packet = header + data

    mySocket.sendto(packet, (destAddr, 1))  # AF_INET address must be tuple, not str
    # Both LISTS and TUPLES consist of a number of objects
    # which can be referenced by their position number within the object


def doOnePing(destAddr, timeout):
    icmp = socket.getprotobyname("icmp")

    mySocket = socket.socket(socket.AF_INET, socket.SOCK_RAW, icmp)

    myID = os.getpid() & 0xFFFF  # Return the current process i
    sendOnePing(mySocket, destAddr, myID)
    delay = receiveOnePing(mySocket, myID, timeout, destAddr)

    mySocket.close()
    return delay


def ping(host, timeout=1):
    # timeout=1 means: If one second goes by without a reply from the server,
    # the client assumes that either the client’s ping or the server’s pong is lost
    # timeout = 1 表示:如果一秒钟没有收到服务器的答复,则客户端会认为客户端的ping或服务器的pong丢失了
    dest = socket.gethostbyname(host)
    print("正在 Ping", host, "[", dest, "] :")
    # Send ping requests to a server separated by approximately one second
    # 将ping请求发送到间隔约一秒钟的服务器
    num = 4
    lost = 0
    delayList = []
    for i in range(num):
        delay = doOnePing(dest, timeout)
        if(type(delay) == str):
            print(delay)
            lost = lost + 1
            continue
        delay = int(delay * 1000)
        delayList.append(delay)
        print("来自", dest, "的回复: 时间=", delay, "ms")
        time.sleep(1)  # one second
    print(dest, "的 Ping 统计信息:")
    print("\t数据包: 已发送 =", num, ",已接收 =", num - lost, ",丢失 =", lost, "(", lost/num * 100, "% 丢失)")
    if(delayList):
        print("往返行程的估计时间(以毫秒为单位):")
        print("\t最短 =", min(delayList), "ms,最长 =", max(delayList), "ms,平均 =", sum(delayList)/len(delayList), "ms")

ping("qq.com")

运行结果:

计算机网络网络层——学习笔记_第14张图片

 

我们分析抓包数据和程序计算的往返时间:

计算机网络网络层——学习笔记_第15张图片

类型8:回显请求(类型对照上表)

计算机网络网络层——学习笔记_第16张图片

类型0:回显回答,过去也解释过程序计算的往返时间比抓包的时间长,因为程序计算的往返时间是包括代码段响应时间,而抓包的时间是产生ICMP协议的时间,不用考虑代码段运行消耗。

计算机网络网络层——学习笔记_第17张图片

你可能感兴趣的:(学习,网络,tcp/ip)