用C++自己实现Ping程序
2018-07-20 来源:open-open
// // Ping.h // #pragma pack(1) #define ICMP_ECHOREPLY 0 #define ICMP_ECHOREQ 8 // IP Header -- RFC 791 typedef struct tagIPHDR { u_char VIHL; // Version and IHL u_char TOS; // Type Of Service short TotLen; // Total Length short ID; // Identification short FlagOff; // Flags and Fragment Offset u_char TTL; // Time To Live u_char Protocol; // Protocol u_short Checksum; // Checksum struct in_addr iaSrc; // Internet Address - Source struct in_addr iaDst; // Internet Address - Destination }IPHDR, *PIPHDR; // ICMP Header - RFC 792 typedef struct tagICMPHDR { u_char Type; // Type u_char Code; // Code u_short Checksum; // Checksum u_short ID; // Identification u_short Seq; // Sequence //char Data; // Data }ICMPHDR, *PICMPHDR; #define REQ_DATASIZE 36 #define REQ_datasize2 30 #define Renum 66 // ICMP Echo Request typedef struct tagECHOREQUEST { ICMPHDR icmpHdr; DWORD dwTime; char cData[REQ_DATASIZE]; }ECHOREQUEST, *PECHOREQUEST; typedef struct tempECHREQUEST{ ICMPHDR icmpHdr; DWORD dwTime; char cData[Renum]; }tempECHREQUEST,*PtempECHOREQUEST; typedef struct tempECHREQUEST2{ ICMPHDR icmpHdr; DWORD dwTime; char cData[Renum+8]; }tempecho,*Ptempecho; // ICMP Echo Reply typedef struct tagECHOREPLY { IPHDR ipHdr; ECHOREQUEST echoRequest; char cFiller[256]; }ECHOREPLY, *PECHOREPLY; //IP echo request typedef struct tagIPEchorequest{ IPHDR ipHeader; ECHOREQUEST icmp_data; }IPECHOREQUET,*PIPECHOREQUEST; typedef struct tagIPEchoTwo{ IPHDR ipHeader; char cData[REQ_datasize2]; }IPEchoTwo,*PIPEchoTwo; typedef struct tagIPechoTwo2{ IPHDR ipHeader; char cData[REQ_datasize2+8]; }IPechotwo2,*PIPechotwo2; //ip echo reply typedef struct tagIPREPLY{ IPHDR ipHdr; ECHOREQUEST echoRequest; char cFiller[256]; }IPECHOREPLY,*PIPECHOREPLY; #pragma pack() // PING.C -- Ping program using ICMP and RAW Sockets // #include <stdio.h> #include <stdlib.h> #include <winsock2.h> #include "ws2tcpip.h" #include <iostream> #pragma comment(lib,"WS2_32.lib") #include "ping.h" // Internal Functions void Ping(LPCSTR pstrHost); void ReportError(LPCSTR pstrFrom); int WaitForEchoReply(SOCKET s); u_short in_cksum(u_short *addr, int len); // ICMP Echo Request/Reply functions int SendEchoRequest(SOCKET, LPSOCKADDR_IN); DWORD RecvEchoReply(SOCKET, LPSOCKADDR_IN, u_char *); //IP internal function void sendIP(LPCSTR pstrHost); int SendIPEchoRequestFragmentTwo(SOCKET s,LPSOCKADDR_IN lpstToAddr); int SendIPEchoRequest(SOCKET, LPSOCKADDR_IN); static short nId = 1; static short nSeq = 1; static short ip_id = rand()%256; static char DataToSend[Renum]; static short flag1 = 0; //标记,0标识不重复,1标识有重复 static short flag2 = 0; //标记,0标识顺序,1标识逆序 char sourseIP[16]; // main() void main(int argc, char **argv) { WSADATA wsaData; WORD wVersionRequested = MAKEWORD(2,1); int nRet; // Check arguments if (argc != 2) { fprintf(stderr,"nUsage: ping hostnamen"); return; } // Init WinSock如果成功,则返回0 nRet = WSAStartup(wVersionRequested, &wsaData); if (nRet) { fprintf(stderr,"nError initializing WinSockn"); return; } // Check version if (wsaData.wVersion != wVersionRequested) { fprintf(stderr,"nWinSock version not supportedn"); return; } printf(" n输入目的主机IP地址:n"); gets(argv[1]); sendIP(argv[1]); system("pause"); // Free WinSock WSACleanup(); } // Ping() // Calls SendEchoRequest() and // RecvEchoReply() and prints results void Ping(LPCSTR pstrHost) { SOCKET rawSocket; //原始套接字 LPHOSTENT lpHost; //保存目的主机信息 struct sockaddr_in saDest; //目的主机 struct sockaddr_in saSrc; //源主机 DWORD dwTimeSent; //发送时间 DWORD dwElapsed; //时间间隔 u_char cTTL; //ttl int nLoop; //发送循环的ping的次数 int nRet; // Create a Raw socket rawSocket = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP); if (rawSocket == SOCKET_ERROR) { ReportError("socket()"); return; } // Lookup host lpHost = gethostbyname(pstrHost); if (lpHost == NULL) { fprintf(stderr,"nHost not found: %sn", pstrHost); return; } // Setup destination socket address saDest.sin_addr.s_addr = *((u_long FAR *) (lpHost->h_addr)); saDest.sin_family = AF_INET; saDest.sin_port = 0; // Tell the user what we're doing printf("nPinging %s [%s] with %d bytes of data:n", pstrHost, inet_ntoa(saDest.sin_addr), REQ_DATASIZE); // Ping multiple times for (nLoop = 0; nLoop < 4; nLoop++) { // Send ICMP echo request SendEchoRequest(rawSocket, &saDest); // Use select() to wait for data to be received nRet = WaitForEchoReply(rawSocket); if (nRet == SOCKET_ERROR) { ReportError("select()"); break; } if (!nRet) { printf("nTimeOut"); break; } // Receive reply dwTimeSent = RecvEchoReply(rawSocket, &saSrc, &cTTL); // Calculate elapsed time dwElapsed = GetTickCount() - dwTimeSent; printf("nReply from: %s: bytes=%d time=%ldms TTL=%d", inet_ntoa(saSrc.sin_addr), REQ_DATASIZE, dwElapsed, cTTL); } printf("n"); nRet = closesocket(rawSocket); if (nRet == SOCKET_ERROR) ReportError("closesocket()"); } /* 使用原始套接字编程,实现分片IP数据包的构造,能够用两个IP分片 构成ICMP ECHO请求,并接收另一方协议栈返回的ICMP应答。 */ void sendIP(LPCSTR pstrHost){ SOCKET rawSocket; //原始套接字 LPHOSTENT lpHost; //保存目的主机信息 struct sockaddr_in saDest; //目的主机 struct sockaddr_in saSrc; //源主机 DWORD dwTimeSent; //发送时间 DWORD dwElapsed; //时间间隔 u_char cTTL; //ttl //int nLoop; //发送循环的ping的次数,这里是需要分片的片数目 int nRet; // Create a Raw socket创建IP数据包的套接字 rawSocket = socket(AF_INET, SOCK_RAW, IPPROTO_IP); if (rawSocket == SOCKET_ERROR) { ReportError("socket()"); return; } //设置套接字选项 BOOL blflag = true; int set_return = setsockopt(rawSocket, IPPROTO_IP, IP_HDRINCL, (char*)&blflag, sizeof(blflag)); if (set_return) { printf("设置选项失败!%d",set_return); return; } if (set_return == SOCKET_ERROR) { printf("设置选项错误!"); return; } // Lookup host lpHost = gethostbyname(pstrHost); if (lpHost == NULL) { fprintf(stderr,"nHost not found: %sn", pstrHost); return; } printf("n输入本机IP地址:n"); gets(sourseIP); // Setup destination socket address saDest.sin_addr.s_addr = *((u_long FAR *) (lpHost->h_addr)); saDest.sin_family = AF_INET; saDest.sin_port = 0; // Tell the user what we're doing printf("n发送数据包(2个分片第一片)到 %s --[%s] 数据大小是%d :n", pstrHost, inet_ntoa(saDest.sin_addr), REQ_DATASIZE); // Ping multiple times printf("n分片发送,不重叠输入0,重叠输入1:n"); std::cin>>flag1; printf("n分片发送,两片顺序发送输入0,逆序发送输入1:n"); std::cin>>flag2; if (flag2==0) { SendIPEchoRequest(rawSocket,&saDest); SendIPEchoRequestFragmentTwo(rawSocket,&saDest); } if (flag2==1) { SendIPEchoRequestFragmentTwo(rawSocket,&saDest); SendIPEchoRequest(rawSocket,&saDest); } // Use select() to wait for data to be received nRet = WaitForEchoReply(rawSocket); if (nRet == SOCKET_ERROR) ReportError("select()"); if (!nRet) printf("nTimeOut"); // Receive reply////////////////////////////////////////////// dwTimeSent = RecvEchoReply(rawSocket, &saSrc, &cTTL); printf("n检查收到的时间:%dn",dwTimeSent); // Calculate elapsed time dwElapsed = GetTickCount() - dwTimeSent; printf("n当前的时间 :%dn",GetTickCount()); printf("nReply from: %s: bytes=%d time=%ldms TTL=%d", inet_ntoa(saSrc.sin_addr), REQ_DATASIZE, dwElapsed, cTTL); printf("n"); nRet = closesocket(rawSocket); if (nRet == SOCKET_ERROR) ReportError("closesocket()"); } // SendEchoRequest() // Fill in echo request header // and send to destination int SendEchoRequest(SOCKET s,LPSOCKADDR_IN lpstToAddr) { static ECHOREQUEST echoReq; static short nId = 1; static short nSeq = 1; int nRet; // Fill in echo request echoReq.icmpHdr.Type = ICMP_ECHOREQ; echoReq.icmpHdr.Code = 0; echoReq.icmpHdr.Checksum = 0; echoReq.icmpHdr.ID = nId++; echoReq.icmpHdr.Seq = nSeq++; // Fill in some data to send for (nRet = 0; nRet < REQ_DATASIZE; nRet++) echoReq.cData[nRet] = 'a'+nRet; // printf("nn%dnn",sizeof(echoReq)); // Save tick count when sent echoReq.dwTime = GetTickCount(); // Put data in packet and compute checksum echoReq.icmpHdr.Checksum = in_cksum((u_short *)&echoReq, sizeof(ECHOREQUEST)); // Send the echo request nRet = sendto(s, /* socket */ (LPSTR)&echoReq, /* buffer */ sizeof(ECHOREQUEST), 0, /* flags */ (LPSOCKADDR)lpstToAddr, /* destination */ sizeof(SOCKADDR_IN)); /* address length */ if (nRet == SOCKET_ERROR) ReportError("sendto()"); return (nRet); } /*发送第一片数据*/ int SendIPEchoRequest(SOCKET s,LPSOCKADDR_IN lpstToAddr){ static IPECHOREQUET ipechoReq; //第一片的IP头部处理过程 ipechoReq.ipHeader.VIHL = 0x45; //版本是4,首部长度是5 ipechoReq.ipHeader.TOS = 0x0; //由标准组织分配 ipechoReq.ipHeader.ID = ip_id; //ip的标识(一唯),这里一定要注意,ip标识不变,同一个数据包的两片 ipechoReq.ipHeader.TTL = 64; //网络中的最大寿命 ipechoReq.ipHeader.iaSrc.s_addr = inet_addr(sourseIP); //源主机地址 ipechoReq.ipHeader.iaDst = lpstToAddr->sin_addr; // ipechoReq.ipHeader.Protocol = IPPROTO_ICMP; //协议的类型包含ICMP ipechoReq.ipHeader.FlagOff = htons(0x2000); //第一片标志位是001000000000000 ipechoReq.ipHeader.TotLen = sizeof(IPECHOREQUET); //总产度设置成64,数据部分应该是52 ipechoReq.ipHeader.Checksum = 0; // //IP包头校验和计算 ipechoReq.ipHeader.Checksum = in_cksum((u_short *)&(ipechoReq.ipHeader), sizeof(IPHDR)); // Fill in echo request ipechoReq.icmp_data.icmpHdr.Type = ICMP_ECHOREQ; //请求数据包类型 ipechoReq.icmp_data.icmpHdr.Code = 0; // ipechoReq.icmp_data.icmpHdr.Checksum = 0; //检验和字段 ipechoReq.icmp_data.icmpHdr.ID = nId++; //成对出现的 ipechoReq.icmp_data.icmpHdr.Seq = nSeq++; //成对出现 // Save tick count when sent ipechoReq.icmp_data.dwTime = GetTickCount(); printf("n发送的时间:%dn",ipechoReq.icmp_data.dwTime); memset(DataToSend,0,Renum); //ICMP头部填充//////////////////////////////////////////////////////////////////////////// int nRet; for (nRet = 0;nRet < REQ_DATASIZE;nRet++) DataToSend[nRet] = 'a'; for(nRet = REQ_DATASIZE;nRet<Renum;nRet++) DataToSend[nRet] = 'b'; //第一片需要发送的数据 for (nRet = 0;nRet < REQ_DATASIZE;nRet++) ipechoReq.icmp_data.cData[nRet] = DataToSend[nRet]; //计算校验和的部分构成 if (flag1 == 0) { tempECHREQUEST tempecho; memset(tempecho.cData,0,Renum); tempecho.icmpHdr = ipechoReq.icmp_data.icmpHdr; tempecho.dwTime = ipechoReq.icmp_data.dwTime; memcpy(tempecho.cData,DataToSend,Renum); // Put data in packet and compute checksum ipechoReq.icmp_data.icmpHdr.Checksum = in_cksum((u_short *)&tempecho, sizeof(tempECHREQUEST)); } if (flag1==1) { tempecho temp; memset(temp.cData,0,Renum+8); temp.dwTime = ipechoReq.icmp_data.dwTime; temp.icmpHdr = ipechoReq.icmp_data.icmpHdr; memcpy(temp.cData,DataToSend,REQ_DATASIZE); //第一片复制 memcpy(temp.cData+REQ_DATASIZE,DataToSend+REQ_DATASIZE-8,REQ_datasize2); ipechoReq.icmp_data.icmpHdr.Checksum = in_cksum((u_short *)&temp, sizeof(tempecho)); } //发送echo 请求 nRet = sendto(s, (LPSTR)&ipechoReq, sizeof(IPECHOREQUET), 0, (LPSOCKADDR)lpstToAddr, sizeof(SOCKADDR_IN)); if (nRet == SOCKET_ERROR) ReportError("sendto()"); return nRet; } /* 发送第二片数据 */ int SendIPEchoRequestFragmentTwo(SOCKET s,LPSOCKADDR_IN lpstToAddr){ IPEchoTwo ipechoReq; //第二片的IP头部处理过程 ipechoReq.ipHeader.VIHL = 0x45; //版本是4,首部长度是5 ipechoReq.ipHeader.TOS = 0x0; //由标准组织分配 ipechoReq.ipHeader.ID = ip_id; //ip的标识(一唯) ipechoReq.ipHeader.TTL = 64; //网络中的最大寿命 ipechoReq.ipHeader.iaSrc.s_addr = inet_addr(sourseIP); //源主机地址 ipechoReq.ipHeader.iaDst = lpstToAddr->sin_addr; // ipechoReq.ipHeader.Protocol = IPPROTO_ICMP; //协议的类型包含ICMP ////////////////////////////////////////////////////////////////////////////////////// if (flag1 == 0) ipechoReq.ipHeader.FlagOff = htons(0x6); //第二片标志位是 if (flag1 == 1) ipechoReq.ipHeader.FlagOff = htons(0x5); //第二片标志位是 ipechoReq.ipHeader.TotLen = sizeof(IPEchoTwo); //总产度设置成64,数据部分应该是44 ipechoReq.ipHeader.Checksum = 0; // //IP包头校验和计算 ipechoReq.ipHeader.Checksum = in_cksum((u_short *)&(ipechoReq.ipHeader), sizeof(IPHDR)); //ICMP头部填充 int nRet; //填充其他的数据部分///////////////////////////////////////////////////////////////////////////// if (flag1 == 0){ memcpy(ipechoReq.cData,DataToSend+REQ_DATASIZE,REQ_datasize2); nRet = sendto(s, (LPSTR)&ipechoReq, sizeof(IPEchoTwo), 0, (LPSOCKADDR)lpstToAddr, sizeof(SOCKADDR_IN)); } if (flag1 == 1){ IPechotwo2 ipecho; ipecho.ipHeader = ipechoReq.ipHeader; memset(ipecho.cData,0,REQ_datasize2+8); memcpy(ipecho.cData,DataToSend+REQ_DATASIZE-8,REQ_datasize2+8); nRet = sendto(s, (LPSTR)&ipechoReq, sizeof(IPechotwo2), 0, (LPSOCKADDR)lpstToAddr, sizeof(SOCKADDR_IN)); } //发送echo 请求 if (nRet == SOCKET_ERROR) ReportError("sendto()"); return nRet; } // RecvEchoReply() // Receive incoming data // and parse out fields DWORD RecvEchoReply(SOCKET s, LPSOCKADDR_IN lpsaFrom, u_char *pTTL) { IPECHOREPLY ipreply; int nRet; int nAddrLen = sizeof(struct sockaddr_in); // Receive the echo reply nRet = recvfrom(s, // socket (LPSTR)&ipreply, // buffer sizeof(IPECHOREPLY), // size of buffer 0, // flags (LPSOCKADDR)lpsaFrom, // From address &nAddrLen); // pointer to address len // Check return value if (nRet == SOCKET_ERROR) ReportError("recvfrom()"); // return time sent and IP TTL *pTTL = ipreply.ipHdr.TTL; return(ipreply.echoRequest.dwTime); } // What happened? void ReportError(LPCSTR pWhere) { fprintf(stderr,"n%s error: %dn", WSAGetLastError()); } // WaitForEchoReply() // Use select() to determine when // data is waiting to be read int WaitForEchoReply(SOCKET s) { struct timeval Timeout; fd_set readfds; /*fd_set结构体的定义 typedef struct fd_set{ u_int fd_count; //集合中的socket数量 SOCKET fd_array[FD_SETSIZE]; //集合中包含的Socket数组 }fd_set; */ readfds.fd_count = 1; readfds.fd_array[0] = s; Timeout.tv_sec = 5; Timeout.tv_usec = 0; return(select(1, &readfds, NULL, NULL, &Timeout)); } // // Mike Muuss' in_cksum() function // and his comments from the original // ping program // // * Author - // * Mike Muuss // * U. S. Army Ballistic Research Laboratory // * December, 1983 /* * I N _ C K S U M * * Checksum routine for Internet Protocol family headers (C Version) * */ u_short in_cksum(u_short *addr, int len) { register int nleft = len; register u_short *w = addr; register u_short answer; register int sum = 0; /* * Our algorithm is simple, using a 32 bit accumulator (sum), * we add sequential 16 bit words to it, and at the end, fold * back all the carry bits from the top 16 bits into the lower * 16 bits. */ while( nleft > 1 ) { sum += *w++; nleft -= 2; } /* mop up an odd byte, if necessary */ if( nleft == 1 ) { u_short u = 0; *(u_char *)(&u) = *(u_char *)w ; sum += u; } /* * add back carry outs from top 16 bits to low 16 bits */ sum = (sum >> 16) + (sum & 0xffff); /* add hi 16 to low 16 */ sum += (sum >> 16); /* add carry */ answer = ~sum; /* truncate to 16 bits */ return (answer); }
标签: 网络
版权申明:本站文章部分自网络,如有侵权,请联系:west999com@outlook.com
特别注意:本站所有转载文章言论不代表本站观点!
本站所提供的图片等素材,版权归原作者所有,如需使用,请与原作者联系。
最新资讯
热门推荐