大家好,我是风筝
轻解网络系列又来了。已有高清 PDF 版本可以离线阅读了,全册 65 页,如果有需要离线版的高清 PDF 可以直接下载。
今天咱们说说 ICMP 协议。ICMP 可谓是网络世界中的最强辅助了,IP数据包如果在途中遭遇不测的话,全靠 ICMP 来通知,要不然丢掉的IP数据包就有如石沉大海,从此杳无音信,发送方也不知道这个包有没有传输成功,倘若没有成功,那失败原因是什么?这些,全靠 ICMP 协议来通知。
ICMP 全称互联网控制报文协议(Internet Cntrol Message Protocol),是网络层的重要协议。

它到底是用来干啥的呢?为啥叫控制报文协议,控制的是什么?
ICMP 分为查询报文和差错报文两大类。查询报文是我们主动发起的,比如ping命令;而差错报文是在发生差错之后要发给源端的,这都是互联网协议模型约定好的。
ICMP的差错报文反馈发生在通信环境中的遇到的各种问题。通过这些信息,使管理者可以对所发生的问题作出诊断,然后采取适当的措施解决。
ICMP的差错报文是整个数据传输链路中非常重要的一个环节。打个比喻,差错报文就是一个只报告坏消息的信使,当数据包在网络中一路畅通的时候,ICMP 差错报文就像隐身了一样,你根本不会知道它的存在,一旦数据包在网络中碰到了各种各样的障碍,这个信使就出来活动了,它的目的只有一个,就是把这个数据包遭遇的不测通知给发送端,但是话术就那么20多种(对应差错代码)。
比如下面两个场景,想必你也有点熟悉吧。
当路由器收到一份IP数据报但又不能转发时,就要发送一份 ICMP「主机不可达」差错报文。
当IP数据报应该被发送到另一个路由器时,收到数据报的路由器就要发送 ICMP「重定向」差错报文给IP数据报的发送端。
虽然工作在网络层,看上去和 IP 协议是并列的,但是 ICMP 报文要附加 IP 头,一般被 IP 层或者更高层的协议(例如TCP或UDP)使用。很少有应用程序直接使用 ICMP 协议,除了 ping、traceroute。

ICMP 协议格式和 IP 协议、TCP 协议这些比起来,那还是非常简单的。

类型
类型字段占用 8 位,主要定义报文的大类,比如类型为 3 统一表示的是不可达,而具体原因是什么则要由代码字段决定。
代码
代码字段同样占用 8 位,代码字段其实就是类型下的子类型,比如上面说了类型为 3 是不可达,代码为 0 表示网络不可达,代码为 1 表示主机不可达。
检验和
用于错误检查,和 IP 协议的检验和的作用一致。
内容
因为类型和代码不同,表示产生差错的原因不同,不同的原因都要有对应的描述,内容这部分就是用来描述产生差错的原因的。
接下来会举几个例子说明。
下面这张图是 ICMP 的分类,包括查询报文和差错报文,需要原始 Excel 的同学可以回复 ICMP获取源文件。

目的不可达是网络传输中经常遇到的问题,各位在开发的过程中可能也碰到过,尤其是做网络编程的时候,经常会碰到,比如连错IP了,比如端口设置错了。
通过上表可知,当类型为 3 的时候,都是不可达的错误,而代码可以从 0 -15,也就是说有16种不可达的具体原因。这种情况下的协议格式是下面这样的。

类型为 3 ,代码 0 - 15。检验和后面有 4 个字节的空间是不使用的,但是必须为0 ,没理由,就这样。
前面说到了内容部分是根据类型和代码不同而不同的。如果是目的不可达,也就是类型是 3 的情况下,内容分为两部分,IP首部和原始IP数据报中数据部分的前 8 个字节。
原始IP数据报中数据部分指的就是TCP或者UDP这些网络层之上的协议,拿 TCP 来说,TCP 是传输层的,当 TCP 数据报到达网络层,会加上 IP 首部,变成一个 IP 数据包。所以这里说的数据部分就是 TCP 数据报,但是这个数据报可能很大,所以只用前 8 个字节就够了,因为前8个字节包含的信息已经足够用了。
回想一下 TCP 协议的格式,前 8 个字节就是下图红框部分,包含源端口和目的端口以及序号。

例如代码为 3 的时候,差错信息是端口不可达,那有了 TCP 协议的前8个字节就能知道导致这个错误的原始数据报文中的目的端口是多少,不可达的端口也就是这个端口。也可以知道原始报文的源端口是多少,有了源端口号就知道这个数据包是哪个用户进程发出来的,就可以交给这个进程对这个差错及时进行处理了。
源端口号是关联用户进程的重要标示,比如我们开发了一个应用,这个应用占用了 8888和8898两个端口,如果安装了这个应用的机器收到了一个差错报文,而差错报文中的内容部分的原始数据包前8个字节拆解后,发现源端口是8898,那就知道这个要交给我们开发的这个应用去处理了。
下面是一个端口不可达的差错报文,用 WireSharek 监测到的格式。
telnet 一个没有开放的端口即可获得 ICMP 端口不可达的差错报文。

将 ICMP 用作查询报文的场景比较少,用作查询报文的意思就像是使用 ARP 协议或者 TCP 协议这种,是我们主动发起的,只不过选了 ICMP 协议。
比如 ping 和 traceroute这两个,之后我们再讲,这两个比较有意思,对 ICMP 应用很巧妙。
另外,可以用作无盘系统启动过程中来获取自身的子网掩码。还可以用作向第三方系统查询当前的时间戳。
了解一下就可以了。
有些场景下是不发送差错报文的,这样做的目的是为了防止ICMP差错报文带来广播风暴。
ICMP差错报文本身发生差错,是不会对差错报文再发送差错报文的。是不是读起来有点绕,TCP 、UDP 出错会发送差错报文,但是 ICMP差错报文在通知源端的过程出错了,那就不管了,要不然可能就没玩没了的发了,比如源端的网线断了。但是, ICMP查询报文可能会产生ICMP差错报文,比如ping命令在传输过程中出错了,源端会收到差错报文。
目的地址是广播地址或多播地址(D类地址)的IP数据报,不发送差错报文。
作为链路层广播的数据报,不发送差错报文,ARP 就是典型的链路层广播数据报。
不是IP分片的第一片,不发送差错报文。数据如果过长,网络层是会进行分片的,这些分片实际上还是同一个数据包的,这种情况下只对第一片发送差错报文,其他分片不管。
源地址不是单个主机的数据报,不发送差错报文。 源地址不能为零地址、环回地址、广播地址或多播地址。
1、 ICMP 在网络层,但要加上 IP 首部;
2、ICMP 分为查询报文和差错报文,主要用到的还是差错报文;
3、ICMP 的差错报文就好像一个只通知坏消息的信息,当数据报在网络中出现问题的时候,及时告知源端,告知的内容包括原因以及产生错误的原始数据报的必要部分;
4、有一些情况是不会发送 ICMP 差错报文的,这样做是为了防止网络风暴;
如果觉得还不错的话,给个推荐吧!
公众号「古时的风筝」,Java 开发者,专注 Java 及周边生态。坚持原创干货输出,你可选择现在就关注我,或者看看历史文章再关注也不迟。长按二维码关注,跟我一起变优秀!

类classAprivatedeffooputs:fooendpublicdefbarputs:barendprivatedefzimputs:zimendprotecteddefdibputs:dibendendA的实例a=A.new测试a.foorescueputs:faila.barrescueputs:faila.zimrescueputs:faila.dibrescueputs:faila.gazrescueputs:fail测试输出failbarfailfailfail.发送测试[:foo,:bar,:zim,:dib,:gaz].each{|m|a.send(m)resc
我有一个模型:classItem项目有一个属性“商店”基于存储的值,我希望Item对象对特定方法具有不同的行为。Rails中是否有针对此的通用设计模式?如果方法中没有大的if-else语句,这是如何干净利落地完成的? 最佳答案 通常通过Single-TableInheritance. 关于ruby-on-rails-Rails-子类化模型的设计模式是什么?,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.co
我正在使用的第三方API的文档状态:"[O]urAPIonlyacceptspaddedBase64encodedstrings."什么是“填充的Base64编码字符串”以及如何在Ruby中生成它们。下面的代码是我第一次尝试创建转换为Base64的JSON格式数据。xa=Base64.encode64(a.to_json) 最佳答案 他们说的padding其实就是Base64本身的一部分。它是末尾的“=”和“==”。Base64将3个字节的数据包编码为4个编码字符。所以如果你的输入数据有长度n和n%3=1=>"=="末尾用于填充n%
我主要使用Ruby来执行此操作,但到目前为止我的攻击计划如下:使用gemsrdf、rdf-rdfa和rdf-microdata或mida来解析给定任何URI的数据。我认为最好映射到像schema.org这样的统一模式,例如使用这个yaml文件,它试图描述数据词汇表和opengraph到schema.org之间的转换:#SchemaXtoschema.orgconversion#data-vocabularyDV:name:namestreet-address:streetAddressregion:addressRegionlocality:addressLocalityphoto:i
为什么4.1%2返回0.0999999999999996?但是4.2%2==0.2。 最佳答案 参见此处:WhatEveryProgrammerShouldKnowAboutFloating-PointArithmetic实数是无限的。计算机使用的位数有限(今天是32位、64位)。因此计算机进行的浮点运算不能代表所有的实数。0.1是这些数字之一。请注意,这不是与Ruby相关的问题,而是与所有编程语言相关的问题,因为它来自计算机表示实数的方式。 关于ruby-为什么4.1%2使用Ruby返
它不等于主线程的binding,这个toplevel作用域是什么?此作用域与主线程中的binding有何不同?>ruby-e'putsTOPLEVEL_BINDING===binding'false 最佳答案 事实是,TOPLEVEL_BINDING始终引用Binding的预定义全局实例,而Kernel#binding创建的新实例>Binding每次封装当前执行上下文。在顶层,它们都包含相同的绑定(bind),但它们不是同一个对象,您无法使用==或===测试它们的绑定(bind)相等性。putsTOPLEVEL_BINDINGput
我可以得到Infinity和NaNn=9.0/0#=>Infinityn.class#=>Floatm=0/0.0#=>NaNm.class#=>Float但是当我想直接访问Infinity或NaN时:Infinity#=>uninitializedconstantInfinity(NameError)NaN#=>uninitializedconstantNaN(NameError)什么是Infinity和NaN?它们是对象、关键字还是其他东西? 最佳答案 您看到打印为Infinity和NaN的只是Float类的两个特殊实例的字符串
如果您尝试在Ruby中的nil对象上调用方法,则会出现NoMethodError异常并显示消息:"undefinedmethod‘...’fornil:NilClass"然而,有一个tryRails中的方法,如果它被发送到一个nil对象,它只返回nil:require'rubygems'require'active_support/all'nil.try(:nonexisting_method)#noNoMethodErrorexceptionanymore那么try如何在内部工作以防止该异常? 最佳答案 像Ruby中的所有其他对象
关闭。这个问题需要detailsorclarity.它目前不接受答案。想改进这个问题吗?通过editingthispost添加细节并澄清问题.关闭8年前。Improvethisquestion为什么SecureRandom.uuid创建一个唯一的字符串?SecureRandom.uuid#=>"35cb4e30-54e1-49f9-b5ce-4134799eb2c0"SecureRandom.uuid方法创建的字符串从不重复?
我在app/helpers/sessions_helper.rb中有一个帮助程序文件,其中包含一个方法my_preference,它返回当前登录用户的首选项。我想在集成测试中访问该方法。例如,这样我就可以在测试中使用getuser_path(my_preference)。在其他帖子中,我读到这可以通过在测试文件中包含requiresessions_helper来实现,但我仍然收到错误NameError:undefinedlocalvariableormethod'my_preference'.我做错了什么?require'test_helper'require'sessions_hel