Ping 第一次总是显示超时

_船长_ 2014-10-10 05:51:01
WinSock编写ping 程序,ping局域网内电脑,第一次总是提示超时,后面几次便正常,如图所示:


百度了一下,大致意思如果对方的MAC地址不存在arp缓冲表中,会发生一个arp请求,导致超时问题,但每次ping时第一次都是失败,对比了一下网上其他人的代码,没有发现我的代码存在什么问题,贴出来大家看一下,帮忙分析下:

IP_HEADER 和 ICMP_HEADER定义:

//IP报文首部结构
typedef struct _tagIPHeader
{
u_char ip_ihl:4; // 首部长度 (4 bits)
u_char ip_ver:4; // 版本 (4 bits)
u_char ip_tos; // 服务类型(Type of service)
u_short ip_tlen; // 总长(Total length)
u_short ip_id; // 标识(Identification)
u_short ip_flags_fo; // 标志位(Flags) (3 bits) + 段偏移量(Fragment offset) (13 bits)
u_char ip_ttl; // 存活时间(Time to live)
u_char ip_proto; // 协议(Protocol)
u_short ip_crc; // 首部校验和(Header checksum)
u_int ip_saddr; // 源地址(Source address)
u_int ip_daddr; // 目的地址(Destination address)
//u_int ip_opandpad; // 选项与填充(Option + Padding)

}IP_HEADER, *PIP_HEADER;

//ICMP报文首部结构(ICMP请求和应答报文)
typedef struct _tagICMPHeader
{
u_char icmp_type; //类型
u_char icmp_code; //代码
u_short icmp_chksum; //校验和
u_short icmp_id; //标识符
u_short icmp_seq; //序列号
u_long icmp_timestamp; //时间戳

}ICMP_HEADER, *PICMP_HEADER;




void Fill_ICMPHeader(char *pBuffer, int nSize, u_short SeqNo)
{
PICMP_HEADER pICMPHeader = (PICMP_HEADER)pBuffer;

pICMPHeader->icmp_type = ICMP_TYPE_ECHO;
pICMPHeader->icmp_code = 0;
pICMPHeader->icmp_chksum = 0;
pICMPHeader->icmp_id = (unsigned short)GetCurrentProcessId();
pICMPHeader->icmp_seq = SeqNo;
pICMPHeader->icmp_timestamp = (unsigned long)::GetTickCount();
pICMPHeader->icmp_chksum = CheckSum((unsigned short*)pICMPHeader, nSize);
}

void Analyze_ICMPPacket(HANDLE hOutputConsole, char *pBuffer, int nSize)
{
DWORD dwTimeStamp = GetTickCount();

PIP_HEADER pIPHeader = (PIP_HEADER)pBuffer;
//计算IP首部长度
u_short nIpHdrSize = pIPHeader->ip_ihl * 4;
if(nSize < nIpHdrSize + 8)
{
//ICMP报文不完整
return;
}

PICMP_HEADER pICMPHeader = (PICMP_HEADER)(pBuffer + nIpHdrSize);
if(ICMP_TYPE_REPLY != pICMPHeader->icmp_type ||
(u_short)GetCurrentProcessId() != pICMPHeader->icmp_id
)
{
//不是ICMP应答报文
return;
}

TCHAR szOutputString[100], szAddrString[16];
QH_AddrToString(AF_INET, (ULONG *)&pIPHeader->ip_saddr, szAddrString);

_stprintf_s(
szOutputString, 100, "来自 %s 的回复:时间=%dms TTL=%d\n",
szAddrString,
dwTimeStamp - pICMPHeader->icmp_timestamp,
pIPHeader->ip_ttl
);

DWORD dwNumOfCharsWritten;
WriteConsole(hOutputConsole, szOutputString, _tcslen(szOutputString), &dwNumOfCharsWritten, NULL);
}

void QH_Ping(DWORD dwAddress, DWORD dwMillsecons)
{
AllocConsole();
DeleteMenu(GetSystemMenu(GetConsoleWindow(), FALSE), SC_CLOSE, MF_BYCOMMAND);
DrawMenuBar(GetConsoleWindow());

sockaddr_in ToAddr = {0}, FromAddr = {0};
DWORD dwNumOfCharsWritten;

HANDLE hOutputConsole = GetStdHandle(STD_OUTPUT_HANDLE);
if(INVALID_HANDLE_VALUE == hOutputConsole)
{
goto _exitprogress;
}

TCHAR szTitleString[100], szAddrString[20];
if(QH_AddrToString(AF_INET, &dwAddress, szAddrString))
{
WriteConsole(hOutputConsole, TRANS_STR, _tcslen(TRANS_STR), &dwNumOfCharsWritten, NULL);
goto _exitprogress;
}
_stprintf_s(szTitleString, 100, PING_STR, szAddrString);
SetConsoleTitle(szTitleString);


//为了设置发送/接收超时,创建的套接字必须使用WSA_FLAG_OVERLAPPED标志
SOCKET hRawSocket = WSASocket(AF_INET, SOCK_RAW, IPPROTO_ICMP, NULL, 0, WSA_FLAG_OVERLAPPED);
if(INVALID_SOCKET == hRawSocket)
{
goto _exitprogress;
}

setsockopt(hRawSocket, SOL_SOCKET, SO_RCVTIMEO, (char *)&dwMillsecons, sizeof(DWORD));
setsockopt(hRawSocket, SOL_SOCKET, SO_SNDTIMEO, (char *)&dwMillsecons, sizeof(DWORD));

DWORD dwAlloc = sizeof(ICMP_HEADER) + 5;
char* pAlloc = new char[dwAlloc];

ToAddr.sin_addr.S_un.S_addr = dwAddress;
ToAddr.sin_family = AF_INET;
ToAddr.sin_port = htons(0);

int i = 0, nSize, nFromLen;
char szRcvBuff[1024];

char szData[] = {
0x41, 0x42, 0x43, 0x44, 0x45
};

while(i < 4)
{
Fill_ICMPHeader(pAlloc, dwAlloc, i);
memcpy(pAlloc + sizeof(ICMP_HEADER), szData, 5);

nSize = sendto(hRawSocket, pAlloc, dwAlloc, 0, (struct sockaddr *)&ToAddr, sizeof(ToAddr));
if(SOCKET_ERROR == nSize)
{
WriteConsole(hOutputConsole, SEND_STR, _tcslen(SEND_STR), &dwNumOfCharsWritten, NULL);
break;
}

nFromLen = sizeof(FromAddr);
nSize = recvfrom(hRawSocket, szRcvBuff, 1024, 0, (struct sockaddr *)&FromAddr, &nFromLen);
if(SOCKET_ERROR == nSize)
{
TCHAR *pErrorStr = RECV_STR;
if(WSAETIMEDOUT == WSAGetLastError())
{
pErrorStr = TMOUT_STR;
}
WriteConsole(hOutputConsole, pErrorStr, _tcslen(pErrorStr), &dwNumOfCharsWritten, NULL);
}
else
{
Analyze_ICMPPacket(hOutputConsole, szRcvBuff, nSize);
}
i++;
}

closesocket(hRawSocket);
delete []pAlloc;

_exitprogress:

system("pause");
FreeConsole();
}
...全文
1435 6 打赏 收藏 转发到动态 举报
写回复
用AI写文章
6 条回复
切换为时间正序
请发表友善的回复…
发表回复
傻X 2014-10-11
  • 打赏
  • 举报
回复
恭喜楼主 刚想和楼主说,可以从抓包入手看看这个问题了。
_船长_ 2014-10-11
  • 打赏
  • 举报
回复
终于知道哪块出问题了,计算校验和时,发送的数据部分也要先进行填充,代码改一下:

//数据部分应先于计算校验和填充即可
memcpy(pAlloc + sizeof(ICMP_HEADER), szData, 5);
Fill_ICMPHeader(pAlloc, dwAlloc, i);
_船长_ 2014-10-11
  • 打赏
  • 举报
回复
引用 3 楼 tiger9991 的回复:
换台机器ping一下,说不定没这问题。
换台机器也是一样的,不管哪台机器上,第一次Ping都是提示“请求超时”,使用系统的ping命令,则没有出现这个问题,奇了怪了,
傻X 2014-10-11
  • 打赏
  • 举报
回复
换台机器ping一下,说不定没这问题。
傻X 2014-10-11
  • 打赏
  • 举报
回复
先要确定是你Ping对象的问题还是自己代码的问题。 System(“Ping命令”)看看效果。
oyljerry 2014-10-11
  • 打赏
  • 举报
回复
直接命令行ping呢。 ping本身就不是稳定的,受网络环境影响等
课程简介:Re:《 Linux 网络管理命令 》  ---------------------------------------内容提要: 01/22)命令 ifconfig:配置或显示网络接口信息02/22)命令 ifup:激活网络接口03/22)命令 ifdown:禁用网络接口04/22)命令 route:显示或管理路由表05/22)命令 arp:管理系统的arp缓存06/22)命令 ip:网络配置工具07/22)命令 netstat:查看网络状态08/22)命令 ss:查看网络状态09/22)命令 ping:测试主机之间网络的连通性10/22)命令 traceroute:追踪数据转发路由状况11/22)命令 arping:发送arp请求12/22)命令 telnet:远程登录主机13/22)命令 nc:多功能网络工具14/22)命令 ssh:协议详解15/22)命令 wget:命令行下载工具16/22)命令 mailq:显示邮件传输队列17/22)命令 mail:发送和接收邮件18/22)命令 nslookup:域名查询工具19/22)命令 dig:域名查询工具20/22)命令 host :域名查询工具21/22)命令 nmap:网络探测工具和安全/端口扫描器22/22)命令 tcpdump:监听网络流量            本人在教学和实战过程中发现,即便是有一定运维经验的人,可能已经能够搭建一定复杂度的Linux架构,但是在来来回回的具体操作中,还是体现出CLI(命令界面)功底不够扎实,甚至操作的非常‘拙’、处处露‘怯’。       对一个士兵来说,枪就是他的武器,对于一个程序员来说,各种library(工具库)就是他的武器;而对于Linux运维人员来说,无疑命令行工具CLI(命令界面)就是他们的武器;高手和小白之间的差距往往就体现在对于这些“武器”的掌握和熟练程度上。有时候一个参数就能够解决的事情,小白们可能要写一个复杂的Shell脚本才能搞定,这就是对CLI(命令界面)没有理解参悟透彻导致。       研磨每一个命令就是擦拭手中的作战武器,平时不保养不理解,等到作战的时候,一定不能够将手中的武器发挥到最好,所以我们要平心、静气和专注,甘坐冷板凳一段时间,才能练就一身非凡的内功!       本教程从实战出发,结合当下流行或最新的Linux(v6/7/8 版本)同时演示,将命令行结合到解决企业实战问题中来,体现出教学注重实战的务实精神,希望从事或未来从事运维的同学,能够认真仔细的学完Linux核心命令的整套课程。       本课程系列将逐步推出,看看我教学的进度和您学习的步伐,孰占鳌头! 注:关于教学环境搭建,可以参考本人其它课程系列,本教学中就不再赘述!     《参透 VMware 桌面级虚拟化》    《在虚拟机中安装模版机(包括应用软件等)》    《SecureCRT 连接 GNS3/Linux 的安全精密工具》  

18,356

社区成员

发帖
与我相关
我的任务
社区描述
VC/MFC 网络编程
c++c语言开发语言 技术论坛(原bbs)
社区管理员
  • 网络编程
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧