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();
}
...全文
1695 6 打赏 收藏 转发到动态 举报
AI 作业
写回复
用AI写文章
6 条回复
切换为时间正序
请发表友善的回复…
发表回复
看不见的裂痕 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命令,则没有出现这个问题,奇了怪了,
看不见的裂痕 2014-10-11
  • 打赏
  • 举报
回复
换台机器ping一下,说不定没这问题。
看不见的裂痕 2014-10-11
  • 打赏
  • 举报
回复
先要确定是你Ping对象的问题还是自己代码的问题。 System(“Ping命令”)看看效果。
oyljerry 2014-10-11
  • 打赏
  • 举报
回复
直接命令行ping呢。 ping本身就不是稳定的,受网络环境影响等

18,363

社区成员

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

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