Answers:
往返时间实际上并未存储在任何地方。发送主机使用ICMP的16位标识符和序列字段来记住发送每个ICMP Echo Request消息的时间。当它收到ICMP Echo Reply时,它记下当前时间,找到它发送了由该响应标识的匹配Request数据包的时间,计算差异并报告。
通常,ping使用ICMP的标识字段来区分多个同时ping,而序列字段则用于区分单个数据包。
取决于实现方式来决定将给定数据包的传出时间存储在何处:通常不是在表中将其存储在主机上,而是通常在传出请求中将其发送并在回复中使用副本来计算时间。(感谢评论者指出这一点。)它以便于实现的任何方式发送,并且当然必须信任远端以及任何介入的设备才能正确复制数据。已知某些系统以16字节(微秒)的分辨率表示时间,有些以8字节(毫秒)的分辨率表示时间。
data
IP数据包部分中的格式是ICMP回显请求/应答消息,该消息从RFC 792 “ Internet控制消息格式”(p14)复制到此处。
Type
对于请求为8,对于答复为0;Code
是0。
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Type | Code | Checksum |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Identifier | Sequence Number |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Data ...
+-+-+-+-+-
PS。为了清楚起见,IP标头的标识字段通常设置为任意值,每个传出数据包都不同,用于重组任何碎片,并且与ICMP主体中的值不相同。
此外,尽管定义了一种机制来将时间戳记作为选项添加到IP报头中,但这不是ping的常规机制,因为很多路由器都配置为不通过某些IP选项。请参阅Internet协议时间戳选项的RFC 781规范。
最后,尽管这里的所有内容都是从IPv4角度编写的,但仍是最初的问题。但是IPv6中的ping非常相似,请参阅ICMPv6 RFC 4443。
ping
在Linux 上至少有一种实现将时间戳存储Data
在ICMP有效负载部分中。当回声答复遍历Internet交换时,这会导致一些非常有趣的错误消息,该Internet交换损坏了每个数据包中该位置的位。
至少使用ping
Linux 上的通用实用程序,发送数据包的时间存储在回显请求数据包的数据部分中,即IP和ICMP标头之后。当接收方回复回音答复时,数据部分保持不变,因此发送方可以计算往返时间。
在ping
实用程序的手册页中对此进行了描述(在“ ICMP数据包详细信息”下):
如果数据空间至少为
struct timeval
ping 大小,则使用该空间的开始字节来包含时间戳记,以用于往返时间的计算。如果数据空间较短,则不会给出往返时间。
在我的机器上sizeof(struct timeval)
是16,因此将数据包数据大小设置为15会阻止ping
显示往返时间:
$ ping -s 15 8.8.8.8
PING 8.8.8.8 (8.8.8.8) 15(43) bytes of data.
23 bytes from 8.8.8.8: icmp_seq=1 ttl=121
当然,如@jonathanjo的答案所描述的那样,将发送时间戳存储在实用程序中也是可能的实现。甚至Linux实用程序也需要一些内部记账,因为它可以检测到重复的数据包。