四 透析ICMP协议: 应用篇ping(RAW Socket)( 四 )


else {
cerr << "error #" << WSAGetLastError() << endl;
}
return -1;
}
return 0;
}
// 对收到的ICMP解码
// 返回值 -2表忽略, -1 表失败, 0 成功
int decode_reply(IPHeader* reply, int bytes, sockaddr_in* from)
{
// 跳过IP包头, 找到ICMP的包头
unsigned short header_len = reply->h_len * 4;
ICMPHeader* icmphdr = (ICMPHeader*)((char*)replyheader_len);
// 包的长度合法, header_lenICMP_MIN为最小ICMP包的长度
if (bytes < header_lenICMP_MIN) {
cerr << "too few bytes from " << inet_ntoa(from->sin_addr) <<
endl;
return -1;
}
// 下面的包类型具体参见我的第一部分 "透析ICMP协议(一): 协议原理"
else if (icmphdr->type != ICMP_ECHO_REPLY) {; //非正常回复
if (icmphdr->type != ICMP_TTL_EXPIRE) {//ttl减为零
if (icmphdr->type == ICMP_DEST_UNREACH) { //主机不可达
cerr << "Destination unreachable" << endl;
}
else {//非法的ICMP包类型
cerr << "Unknown ICMP packet type " << int(icmphdr->type) <<
" received" << endl;
}
return -1;
}
}
else if (icmphdr->id != (USHORT)GetCurrentProcessId()) {
//不是本进程发的包, 可能是同机的其它ping进程发的
return -2;
}

;// 指出包传递了多远
// [bugfree]我认为作者这里有问题, 因为有些系统的ttl初值为128如winXP,
// 有些为256如我的DNS服务器211.97.168.129, 作者假设为256有点武断,
// 可以一起探讨这个问题, 回email:zhangliangsd@hotmail.com
int nHops = int(256 - reply->ttl);
if (nHops == 192) {
// TTL came back 64, so ping was probably to a host on the
// LAN -- call it a single hop.
nHops = 1;
}
else if (nHops == 128) {
// Probably localhost
nHops = 0;
}
// 所有工作结束,打印信息
cout << endl << bytes << " bytes from " <<
inet_ntoa(from->sin_addr) << ", icmp_seq " <<
icmphdr->seq << ", ";
if (icmphdr->type == ICMP_TTL_EXPIRE) {
cout << "TTL expired." << endl;
}
else {
cout << nHops << " hop" << (nHops == 1 ? "" : "s");
cout << ", time: " << (GetTickCount() - icmphdr->timestamp) <<
" ms." << endl;
}
return 0;
}
总结和建议:
-----------
bugfree建议其中的这些方面需要改进:
1. 头文件iostream.h 改为 iostream, 后者是标准C的头文件
同时添加对std::cout 和 std::endl;的引用
对于cerr 建议都改为std::cout(因为后者头文件不支持)
2. 程序的发送和接受采用了同步的方式, 这使得假如出现网络问题recv_ping将陷入持续等待.
这是我们不想看到的.
这三种技术可以达到目的:
- 使用多线程, 将ping封装进线程, 在主程序中对它的超时进行处理
- 使用select()函数来实现
- 使用windows的 WSAAsyncSelect()
这里对这些方法不作具体讨论, 留给读者自已完成.

推荐阅读