这些天在学习windows网络编程,昨天看到了探测网络中的在线设备,其中刚好有一个实现ping命令的实例,就照着
拍了下代码,结果老是超时,后来到网上问才知道少了计算检验和,于是网上找了下关于检验和的知识。
校验和计算:
为了计算一份数据报的校验和码。首先把校验和字段置为0。然后,对首部中每一个16bit进行二进制反码求和,结果存在校验和字段中。当受到到一份ip数据报后,同样对首部中每个16bit进行二进制反码的求和。由于接收方在计算过程中包含了发送方存在首部中的检验和,因此,如果首部在传输过程中没有任何差错,那么接收方计算的结果应该为全1.
Icmp 校验和的计算:
TCP/ip协议对校验和计算方法:对16位的数据进行累加计算,并返回求反的计算结果。
需要注意的是对奇数个字节数据的计算。是将最后的有效数据作为最高位的字节,低字节填充为0.
实现代码如下
USHORT checksum(USHORT *buffer, int size) { unsigned long cksum=0; while(size > 1) { cksum += *buffer++; size -= sizeof(USHORT); } if(size) { cksum += *(UCHAR*)buffer; } cksum = (cksum >> 16) + (cksum & 0xffff); cksum += (cksum >>16); return (USHORT)(~cksum); }
#include<winsock2.h> #include<stdio.h> #include<iostream> using namespace std; #define ICMP_ECHO 8 #define ICMP_ECHOREPLY 0 #define ICMP_MIN 8 #define DEF_PACKET_SIZE 32 #define MAX_PACKET 1024 typedef struct iphdr { unsigned int h_len:4; unsigned int version:4; unsigned char tos; unsigned short total_len; unsigned short ident; unsigned short frag_and_flags; unsigned char ttl; unsigned char proto; unsigned short checksun; unsigned int sourceIP; unsigned int destIP; }IpHeader; typedef struct _ihdr { BYTE i_type; BYTE i_code; USHORT i_cksum; USHORT i_id; USHORT i_seq; ULONG timestamp; }IcmpHeader; void fill_icmp_date (char* icmp_data, int datasize); int decode_resp (char* buf, int bytes, struct sockaddr_in* from, DWORD tid); int ping (const char* ip, DWORD timeout); USHORT checksum(USHORT *buffer, int size); int main(int argc, char* argv[]) { if (argc != 2) { printf ("参数数量不正确。请指定要ping的IP地址。\n"); return -1; } /*char argv[2][20]; argv[1] = "192.168.1.102";*/ printf ("ping %s\n", argv[1]); int ret = ping (argv[1], 500); if (ret >= 0) printf ("%s在线,执行ping操作用时%dms。\n", argv[1], ret); else { switch (ret) { case -1: printf ("ping 超时...\n"); break; case -2: printf ("创建Socket 出错...\n"); break; case -3: printf ("设置Socket的接收超时选项出错...\n"); break; case -4: printf ("设置Socket的发送超时选项出错...\n"); break; case -5: printf ("获取域名出错,可能是IP地址不正确...\n"); break; case -6: printf ("未能为ICMP数据包分配到足够的空间...\n"); break; case -7: printf ("发送ICMP数据包出错...\n"); break; case -8: printf ("发送ICMP数据包的数量不正确...\n"); break; case -9: printf ("接收ICMP数据包出错...\n"); break; case -1000: printf ("初始化Windows Sockets环境出错...\n"); break; default: printf ("未知的错误"); break; } } printf ("\n"); system ("pause"); return 0; } USHORT checksum(USHORT *buffer, int size) { unsigned long cksum=0; while(size > 1) { cksum += *buffer++; size -= sizeof(USHORT); } if(size) { cksum += *(UCHAR*)buffer; } cksum = (cksum >> 16) + (cksum & 0xffff); cksum += (cksum >>16); return (USHORT)(~cksum); } void fill_icmp_data (char* icmp_data, int datasize) { IcmpHeader* imcp_hdr; char* datapart; imcp_hdr = (IcmpHeader*)icmp_data; imcp_hdr->i_type = ICMP_ECHO; imcp_hdr->i_code = 0; imcp_hdr->i_id = (USHORT)GetCurrentThreadId(); imcp_hdr->i_cksum = 0; imcp_hdr->i_seq = 0; datapart = icmp_data + sizeof(IcmpHeader); memset (datapart, 'E', datasize - sizeof(IcmpHeader)); } int decode_resp (char* buf, int bytes, struct sockaddr_in* from, DWORD tid) { IpHeader* iphdr; IcmpHeader* icmphdr; unsigned short iphdrlen; iphdr = (IpHeader*)buf; iphdrlen = iphdr->h_len * 4; if (bytes < iphdrlen + ICMP_MIN) return -1; icmphdr = (IcmpHeader*)(buf + iphdrlen); if (icmphdr->i_type != ICMP_ECHOREPLY) return -2; if (icmphdr->i_id != (USHORT)tid) return -3; int time = GetTickCount() - icmphdr->timestamp; if (time >= 0) return time; else return -4; } int ping (const char* ip, DWORD timeout) { WSADATA wsaData; SOCKET sockRaw = NULL; struct sockaddr_in dest, from; struct hostent* hp; int datasize; char* dest_ip = NULL; char* icmp_data = NULL; char* recvbuf = NULL; USHORT seq_no = 0; int ret = -1; if (WSAStartup (MAKEWORD(2, 1), &wsaData) != 0) { ret = -100; goto FIN; } sockRaw = WSASocket (AF_INET, SOCK_RAW, IPPROTO_ICMP, NULL, 0, WSA_FLAG_OVERLAPPED); if (sockRaw == INVALID_SOCKET) { ret = -2; goto FIN; } int bread = setsockopt (sockRaw, SOL_SOCKET, SO_RCVTIMEO, (char*)&timeout, sizeof(timeout)); if (bread == SOCKET_ERROR) { ret = -3; goto FIN; } bread = setsockopt (sockRaw, SOL_SOCKET, SO_SNDTIMEO, (char*)&timeout, sizeof(timeout)); if (bread == SOCKET_ERROR) { ret = -4; goto FIN; } memset (&dest, 0, sizeof(dest)); unsigned int addr = 0; hp = gethostbyname (ip); if (!hp) addr = inet_addr (ip); if ((!hp) && (addr == INADDR_NONE)) { ret = -5; goto FIN; } /*配置远程通信地址*/ if (hp != NULL) memcpy (&(dest.sin_addr), hp->h_addr_list[0], hp->h_length); else dest.sin_addr.S_un.S_addr = addr; if (hp) dest.sin_family = hp->h_addrtype; else dest.sin_family = AF_INET; dest_ip = inet_ntoa (dest.sin_addr); /*准备发送数据*/ datasize = DEF_PACKET_SIZE; datasize += sizeof(IcmpHeader); char icmp_dataStack[MAX_PACKET]; char recvbufStack[MAX_PACKET]; icmp_data = icmp_dataStack; recvbuf = recvbufStack; //memset (recvbuf, 0, MAX_PACKET); if (!icmp_data) { ret = -6; goto FIN; } memset (icmp_data, 0, MAX_PACKET); fill_icmp_data (icmp_data, datasize); ((IcmpHeader*)icmp_data)->i_cksum = 0; DWORD startTime = GetTickCount(); ((IcmpHeader*)icmp_data)->timestamp = startTime; ((IcmpHeader*)icmp_data)->i_seq = seq_no++; ((IcmpHeader*)icmp_data)->i_cksum = checksum ((USHORT*)icmp_data, datasize); /*发送数据*/ int bwrote; bwrote = sendto (sockRaw, icmp_data, datasize, 0, (struct sockaddr*)&dest, sizeof(dest)); if (bwrote == SOCKET_ERROR) { if (WSAGetLastError() == WSAETIMEDOUT) { ret = -7; goto FIN; } } if (bwrote < datasize) { ret = -8; goto FIN; } LARGE_INTEGER ticksPerSecond; LARGE_INTEGER start_tick; LARGE_INTEGER end_tick; double elapsed; QueryPerformanceFrequency (&ticksPerSecond); QueryPerformanceCounter (&start_tick); int fromlen = sizeof(from); while (1) { bread = recvfrom (sockRaw, recvbuf, MAX_PACKET, 0, (struct sockaddr*)&from, &fromlen); if (bread == SOCKET_ERROR) { if (WSAGetLastError() == WSAETIMEDOUT) { ret = -1; goto FIN; } ret = -9; goto FIN; } /*对回应的IP数据包进行解析,定位ICMP数据*/ int time = decode_resp (recvbuf, bread, &from, GetCurrentThreadId()); if (time >= 0) { QueryPerformanceCounter (&end_tick); elapsed = ((double)(end_tick.QuadPart - start_tick.QuadPart) / ticksPerSecond.QuadPart); ret = (int)(elapsed * 1000); goto FIN; } else if (GetTickCount() - startTime >= timeout || GetTickCount() < startTime) { ret = -1; goto FIN; } } FIN: closesocket (sockRaw); WSACleanup (); return ret; }