本文实例讲述了C++实现ping程序的方法。分享给大家供大家参考。具体实现方法如下:
该实例涉及ICMP数据包的发送与回显,PING程序代码如下:
DWORD WINAPI ThreadProc(LPVOID lParam) { CInitSock initSock; HWND hWnd = (HWND)lParam; //从参数得到句柄 char szIp[64] ={0}; ::GetDlgItemTextA(hWnd, IDC_IP, szIp, sizeof(szIp)); //从控件得到ip地址 //1. 创建协议类型为IPPROTO_ICMP的原始套接字,设置套接字属性 SOCKET sRaw = ::socket(AF_INET, SOCK_RAW, IPPROTO_ICMP); SetTimeOut(sRaw, 1000, TRUE);//2.创建并初始化ICMP封包 char buff[sizeof(ICMP_HDR)+32] = {0}; ICMP_HDR* pIcmp = (ICMP_HDR*)buff; pIcmp->icmp_type = 8; pIcmp->icmp_code = 0; pIcmp->icmp_checksum = 0; pIcmp->icmp_id = (USHORT)::GetCurrentProcessId(); pIcmp->icmp_sequence = 0; pIcmp->icmp_timestamp = 0; //填充数据 memset(&buff[sizeof(ICMP_HDR)], 'E', 32);
//开始发送和接收封包 USHORT nSeq = 0; SOCKADDR_IN dest; dest.sin_family = AF_INET; dest.sin_port = htons(0); dest.sin_addr.S_un.S_addr = inet_addr(szIp); //inet_addr("192.168.19.63"); //inet_addr(szIp);220.181.111.147 CString strToShow=""; char* pszRevBuf = new char[1024]; //[1024] = {0}; memset(pszRevBuf, 0, 1024); SOCKADDR_IN from; int nFromLen= sizeof(from); while (TRUE) { //3. 调用sendto发送ICMP请求 static int nCount=0; if (nCount++==4) { break; } pIcmp->icmp_checksum = checksum((USHORT*)buff, sizeof(ICMP_HDR)+32); pIcmp->icmp_sequence = ++nSeq; pIcmp->icmp_timestamp = ::GetTickCount(); int nRet; nRet = ::sendto(sRaw, buff, sizeof(ICMP_HDR)+32, 0, (SOCKADDR*)&dest, sizeof(dest)); if (nRet == SOCKET_ERROR) { int nError = ::WSAGetLastError(); strToShow += "sendto error\r\n"; return -1; } //4. 调用recvfrom接受ICMP响应 nRet = ::recvfrom(sRaw, pszRevBuf, 1024, 0, (sockaddr*)&from, &nFromLen); if (nRet == SOCKET_ERROR) { if (::WSAGetLastError() == WSAETIMEDOUT) { strToShow += "timeout\r\n"; continue; } strToShow += "recvfrom error\r\n"; return -1; } //解析接收到的ICMP包 int nTick = ::GetTickCount(); if (nRet < sizeof(ICMP_HDR)+sizeof(IPHeader)) { strToShow += "less byte recved..\r\n"; } ICMP_HDR* pRecvIcmp = (ICMP_HDR*)(pszRevBuf + sizeof(IPHeader));// 跳过IP头 if (pRecvIcmp->icmp_type != 0) { strToShow += "不是回显类型"; return -1; } if (pRecvIcmp->icmp_id != ::GetCurrentProcessId()) { strToShow += "不是本进程的回显包\r\n"; return -1; } CString strTemp; strTemp.Format("收到来自于[%s]IP的%04d字节\r\n", inet_ntoa(from.sin_addr), nRet); strToShow += strTemp;
strTemp = ""; strTemp.Format("序列号:%d\r\n", pRecvIcmp->icmp_sequence); strToShow += strTemp;
strTemp = ""; strTemp.Format("花费时间:%d\r\n", nTick - pRecvIcmp->icmp_timestamp); } strToShow += "**********************************"; ::SetDlgItemTextA(hWnd, IDC_DATA, strToShow); return 0; }
IP头和ICMP头代码如下:
USHORT checksum(USHORT* buff, int nSize) //222.89.166.13 { unsigned long cksum=0; //将数据以字为单位加到cksum while (nSize > 1) { cksum += *buff++; nSize -= sizeof(USHORT); }//如果为奇数, 将最后一个字扩展到双字,再累加 if (nSize) { cksum += *(UCHAR*)buff; } //将cksum的高16位与低16位相加,取反后得到校验和 cksum = (cksum>>16) + (cksum & 0xffff); cksum += (cksum>>16); return (USHORT)(~cksum); } BOOL SetTimeOut(SOCKET s, int nTime, BOOL bRecv) { int ret = ::setsockopt(s, SOL_SOCKET, bRecv?SO_RCVTIMEO:SO_SNDTIMEO, (char*)&nTime, sizeof(nTime)); return ret!=SOCKET_ERROR; } typedef struct icmp_hdr { unsigned char icmp_type; unsigned char icmp_code; unsigned short icmp_checksum; //下面是回显头 unsigned short icmp_id; unsigned short icmp_sequence; unsigned long icmp_timestamp; }ICMP_HDR, *PICMP_HDR;
typedef struct _IPHeader { UCHAR iphVerLen; UCHAR ipTOS; USHORT ipLength; USHORT ipID; USHORT ipFlag; UCHAR ipTTL; UCHAR ipProtocol; USHORT ipCheckSum; ULONG ipSource; ULONG ipDestination; }IPHeader, *PIPHeader;
希望本文所述对大家的C++程序设计有所帮助。