如何计算 ICMP 数据包的往返时间

Posted

技术标签:

【中文标题】如何计算 ICMP 数据包的往返时间【英文标题】:How to calculate round trip time for an ICMP packet 【发布时间】:2013-11-24 00:32:04 【问题描述】:

我正在 Qt5 中编写一些 C 代码来发送 ICMP 回显数据包以检测机器。我不知道如何计算往返时间。

我的代码似乎无法正常工作:

Before sending:
struct timezone tz;
struct timeval  ts;
gettimeofday( &ts, &tz );
ts.tv_sec  = ts.tv_sec;
ts.tv_usec = ts.tv_usec;
bcopy(&ts, &(pkt.icmp.data[0]), sizeof(struct timeval)); // target host will modify this
bcopy(&ts, &(pkt.icmp.data[8]), sizeof(struct timeval));

After receiving:
struct timezone tz;
struct timeval  ts1;
struct timeval  ts2;

bcopy(&(pkt.icmp.data[8]), &ts1, sizeof(struct timeval));
gettimeofday( &ts2, &tz );
round trip time = (ts2.tv_sec - ts1.tv_sec) +
                     1e-6 * (ts2.tv_usec - ts1.tv_usec);

有什么问题吗?

谢谢

编辑: 这是接收函数:

void CPingReceiver::dataProcess(struct icmp_packet pkt)

struct timezone tz;
struct timeval  ts1;
struct timeval  ts2;

bcopy(&(pkt.icmp.data[8]), &ts1, sizeof(struct timeval));
gettimeofday( &ts2, &tz );

QHostAddress ha  = QHostAddress(ntohl(pkt.ip.saddr));
foundItem.first  = ha.toString();// (ts2.tv_sec * 1000 + ts2.tv_usec / 1000) - (ts1.tv_sec * 1000 + ts1.tv_usec / 1000)
foundItem.second = tr("%1 ms").arg(((ts2.tv_sec - ts1.tv_sec) +
                                   (ts2.tv_usec - ts1.tv_usec) / 1000000));

emit sendToListener(foundItem);

// qDebug() << addr << endl;

// now send the data to ARP Worker Singleton
// PING results will send its data to ARP Worker Singleton as well
// same for hostname, vendor and netbios, open ports

这里是发送函数:

/************************************************************************
 * Build ICMP Header
 ************************************************************************/
pkt.icmp.type       = ICMP_ECHO;        // icmp echo */
pkt.icmp.code       = 0;                // only valid value for echo or echo reply */
pkt.icmp.checksum   = 0;
pkt.icmp.identifier = ICMP_IDENTIFIER;  // the id we'll be using to distinguish our data from other icmp packets */
pkt.icmp.sequence   = 1;                // Start from 0
struct timezone tz;
struct timeval  ts;
gettimeofday( &ts, &tz );
bzero(pkt.icmp.data, ICMP_MTU);
bcopy(&ts, &(pkt.icmp.data[0]), sizeof(struct timeval));
bcopy(&ts, &(pkt.icmp.data[8]), sizeof(struct timeval));

pkt.icmp.checksum   = calcsum((quint16 *)(&pkt.icmp), sizeof(pkt.icmp));

这是我得到的:

"192.168.0.21" "----" "F0:7D:68:04:49:86"  // ARP reply
"192.168.0.28" "----" "00:19:5B:0D:30:85"  // ARP reply
"192.168.0.30" "----" "00:04:20:2C:83:34"   // ARP reply
"-------------PING reply-----------------" "192.168.0.21" "----" "-8316290828429 ms" 
"192.168.0.26" "----" "74:44:01:D3:07:E0"   // ARP reply
"-------------PING reply---------------" "192.168.0.26" "----" "-8316290828429 ms" 
"-------------PING reply---------------" "192.168.0.30" "----" "-8316290828429 ms" 
"192.168.0.23" "----" "C8:60:00:1A:B0:BC"   // ARP reply
"-------------PING reply---------------" "192.168.0.23" "----" "-8316290828429 ms"

【问题讨论】:

你可以使用timersub函数。 结构时区 tz;结构时间 ts1;结构时间 ts2;结构时间 ts3; bcopy(&(pkt.icmp.data[8]), &ts1, sizeof(struct timeval)); gettimeofday( &ts2, &tz );定时器子(&ts2,&ts1,&ts3); QHostAddress ha = QHostAddress(ntohl(pkt.ip.saddr)); foundItem.first = ha.toString(); foundItem.second = tr("%1 ms").arg(ts3.tv_usec);"192.168.0.30" "----" "-8316290828429423476 ms" 为什么不用你填写的ts变量,而不是网络数据? bcopy 的文档说改用 memmove。 (在这里你可以放心使用memcpy 【参考方案1】:

gettimeofday() 函数以本机字节顺序提供时间值,不一定是网络字节顺序。请勿不要致电ntohl()

double round_trip_time = (ts2.tv_sec - ts1.tv_sec) +
                         1e-6 * (ts2.tv_usec - ts1.tv_usec);

【讨论】:

我得到了这个:"192.168.0.21" "----" "-5.37912e+17 ms" 这是一个非常可疑的数字。大约是 10^48 秒,这意味着你有一个 64 位的秒数,这已经搞砸了。确保永远不要对 64 位值使用 htonl(),因为 htonl() 对 32 位值进行操作。 我正在运行 64 位 fedora 18,但我已经删除了 htonl。谢谢! 我这样做了:foundItem.second = tr("%1 ms").arg((quint32)(((ts2.tv_sec - ts1.tv_sec) + (ts2.tv_usec - ts1.tv_usec ) / 1000000)));但仍然得到:“192.168.0.30”“----”“3060823923 s”【参考方案2】:

你有没有想过使用QElapsedTimer?

【讨论】:

以上是关于如何计算 ICMP 数据包的往返时间的主要内容,如果未能解决你的问题,请参考以下文章

30 伪造ICMP数据包的Ethernet层

简单了解ICMP协议

关于ICMP数据包的问题

29 伪造ICMP数据包的IP层

29 伪造ICMP数据包的IP层

Linux下网络排查之ping|traceroute|mtr工具(zz)