windows平臺Ping的簡單實現
阿新 • • 發佈:2017-12-25
alloc 序號 decode wsad sage 接口 .com 使用 name
//Ping.h
#pragma once #define _CRT_SECURE_NO_WARNINGS #define _CRT_NONSTDC_NO_DEPRECATE 1 #pragma pack(1)//一定要把字節對齊數設為1 #include <windows.h> #define SECS_TO_FT_MULT 10000000 #define STATUS_FAILED 0xFFFF #define DEF_PACKET_SIZE 32 #define ICMP_ECHO 8 #define ICMP_ECHOREPLY 0 #define MAX_PACKET 128 #defineView CodeICMP_MIN 8 enum SOCKET_TYPE{ TCP, RAW }; // The IP header typedef struct iphdr { unsigned char hLen : 4; // length of the header unsigned char version : 4; // Version of IP unsigned char tos; // Type of service unsigned short totalLen; // total length of the packet unsigned shortident; // unique identifier unsigned short frag_and_flags; // flags unsigned char ttl; unsigned char proto; // protocol (TCP, UDP, ICMP, IGMP etc) unsigned short checksum; // IP checksum unsigned int sourceIP; unsigned int destIP; }IpHeader; // ICMP header typedef struct icmphdr { BYTE type;//消息類型 BYTE code; //代碼 /* type sub code */ USHORT cksum; //校驗和 USHORT id; //ID號 USHORT seq; //序列號 ULONG timestamp; //時間戳 }IcmpHeader; //ICMP報文 包括報頭和數據 //暫未用到此頭部 typedef struct tcphdr{ USHORT sPort; //源端口號 USHORT dPort; //目的端口號 UINT seq; //32位序號 UINT ackSeq; //32位確認序號 UINT headerLen : 4; //4位首部長度 UINT other1 : 28; USHORT cksum; //16位檢驗和 USHORT other2; UCHAR kind; //tcp頭部選項 UCHAR len; ULONG timestamp; ULONG echoTimestamp; }TcpHeader; //ping的結果 typedef struct pingreply{ USHORT seq; DWORD rtt;//往返時間 DWORD bytes; DWORD ttl; }PingReply; void fillTcpData(char*, int); void fillIcmpData(char*, int); USHORT checkSum(USHORT*, int); void decodeTcpResp(char*, int, struct sockaddr_in*); void decodeIcmpResp(char*, int, struct sockaddr_in*); sockaddr_in validateArgs(int, char* []); void gettimeofday(struct timeval*, void*);
//Ping.c
#include "Ping.h" #include <stdio.h> #include <stdlib.h> #include <time.h> char* args[32]; static int index = 0; int type = RAW; void fillIcmpData(char *icmp_data, int datasize) { IcmpHeader* icmp_hdr; char* datapart; static int sseq = 1; icmp_hdr = (IcmpHeader*)icmp_data; //填充ICMP Header部分 icmp_hdr->type = ICMP_ECHO; icmp_hdr->code = 0; icmp_hdr->id = (USHORT)GetCurrentProcessId(); icmp_hdr->cksum = 0;//初始為0 icmp_hdr->seq = sseq++;//初始為0 struct timeval tv2; gettimeofday(&tv2, NULL); icmp_hdr->timestamp = tv2.tv_sec * 1000 + tv2.tv_usec / 1000; datapart = icmp_data + sizeof(IcmpHeader); //填充ICMP Data部分 memset(datapart, ‘E‘, datasize - sizeof(IcmpHeader)); } 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 decodeIcmpResp(char* buf, int bytes, struct sockaddr_in* from) { /*unsigned char* tmp = (unsigned char*)buf; for (int i = 0; i < bytes; ++i) { printf("%02x", *tmp); ++tmp; } printf("\n");*/ IpHeader* ipheader; IcmpHeader* icmpheader; unsigned short ipheaderlen; ULONG recvtimestamp; PingReply pingreply; ipheader = (IpHeader*)buf; ipheaderlen = ipheader->hLen * 4; struct timeval tv; gettimeofday(&tv, NULL); recvtimestamp = tv.tv_sec * 1000 + tv.tv_usec / 1000; if (bytes < ipheaderlen + ICMP_MIN) { printf("Too few bytes from %s \n", inet_ntoa(from->sin_addr)); return; } icmpheader = (IcmpHeader*)(buf + ipheaderlen); if (icmpheader->type != ICMP_ECHOREPLY || icmpheader->code != 0) { printf("echo packet type is %d,code is %d...\n", icmpheader->type, icmpheader->code); return; } if (icmpheader->id != (USHORT)GetCurrentProcessId()) { printf("others proc packet...\n"); return; } pingreply.seq = icmpheader->seq; pingreply.bytes = bytes - ipheaderlen - sizeof(IcmpHeader)+sizeof(icmpheader->timestamp); pingreply.ttl = ipheader->ttl; pingreply.rtt = (recvtimestamp - icmpheader->timestamp); //printf("%d--->%d\n", recvtimestamp, icmpheader->timestamp); printf("Reply %d bytes from %s :", pingreply.bytes, inet_ntoa(from->sin_addr)); printf("icmp_seq = %d ", pingreply.seq); printf("time = %dms ", pingreply.rtt); printf("ttl = %d\n", pingreply.ttl); } sockaddr_in validateArgs(int argc, char* argv[]) { argc = 6; argv[1] = "-i"; argv[2] = "0000"; argv[3] = "-n"; argv[4] = "10"; //argv[5] = "61.135.169.121:80"; //argv[5] = "61.135.157.156:80"; argv[5] = "10.99.1.86:8888"; //argv[5] = "10.10.10.3"; //argv[5] = "www.baidu.com:80"; if (argc < 2 || argc % 2 != 0) { printf("usage: ./ping [-i|-n] [counts] host[:port]\n"); ExitProcess(STATUS_FAILED); } char* address = argv[1]; char host[128]; memcpy(host, address, strlen(address) + 1); char* port = host; memset(args, ‘\0‘, sizeof(args) / sizeof(args[0])); for (int i = 1; i < argc; ++i) { if (i % 2 == 1) { if (argv[i][0] == ‘-‘ && argv[i][1] >= ‘a‘ && argv[i][1] <= ‘z‘){ char* start = &(argv[i][1]); args[index++] = start; } else{ address = argv[i]; int len = strlen(address) + 1; memcpy(host, address, strlen(address) + 1); args[index++] = address; port = host; while (*port) { if (*port == ‘:‘) { *port = ‘\0‘; ++port; break; } ++port; } } } else{ if (argv[i][0] >= ‘0‘ && argv[i][0] <= ‘9‘) { char* start = &(argv[i][0]); args[index++] = start; } else{ ExitProcess(STATUS_FAILED); } } } for (int i = 0; i < index; ++i) { printf("%s ", args[i]); } printf("\n"); struct sockaddr_in dest; memset(&dest, 0, sizeof(dest)); struct hostent* hp = NULL; unsigned int addr = 0; hp = gethostbyname(host); if (!hp) { printf("host error...\n"); ExitProcess(STATUS_FAILED); } else { addr = inet_addr(host); } if ((!hp) && (addr == INADDR_NONE)) { ExitProcess(STATUS_FAILED); } if (hp) { memcpy(&dest.sin_addr, hp->h_addr_list[0], hp->h_length); dest.sin_family = hp->h_addrtype; } else { dest.sin_addr.s_addr = addr; dest.sin_family = AF_INET; } if (*port != ‘\0‘) { dest.sin_port = htons(atoi(port)); type = TCP; } return dest; } void gettimeofday(struct timeval* tp, void* tzp) { time_t clock; struct tm tm; SYSTEMTIME wtm; GetLocalTime(&wtm); tm.tm_year = wtm.wYear - 1900; tm.tm_mon = wtm.wMonth - 1; tm.tm_mday = wtm.wDay; tm.tm_hour = wtm.wHour; tm.tm_min = wtm.wMinute; tm.tm_sec = wtm.wSecond; tm.tm_isdst = -1; clock = mktime(&tm); //tm結構表示的時間轉化為日歷時間 tp->tv_sec = clock; tp->tv_usec = wtm.wMilliseconds * 1000; }View Code
//main.c
#include <winsock2.h> #include <stdlib.h> #include <stdio.h> #include <time.h> #include "Ping.h" #pragma comment(lib,"ws2_32.lib") extern int type; extern char* args[32]; int main(int argc, char* argv[]) { //指明要使用的庫 #ifdef WIN32 WSADATA wsaData; if (0 != WSAStartup(MAKEWORD(2, 2), &wsaData)) { fprintf(stderr, "WSAStartup failed: %d\n", GetLastError()); ExitProcess(STATUS_FAILED); } #endif //參數解析 sockaddr_in sockaddrDest = validateArgs(argc, argv); int sockaddrDestlen = sizeof(sockaddrDest); int defaulti = 1; int defaultn = 4; int i = 0; while (args[i] != ‘\0‘) { if (0 == strcmp(args[i], "i")) { defaulti = atoi(args[++i]); } else if (0 == strcmp(args[i], "n")) { defaultn = atoi(args[++i]); } else{ } ++i; } args; //建立socket //原始套接字(SOCK——RAW):沒有經過處理的IP數據包,可以根據自己程序的要求進行封裝 //為防止普通用戶向網絡寫自己的IP數據包,只有管理員才有權創建原始套接口 SOCKET sock; ULONG starttime, endtime; int n = 3; if (type == RAW){ for (int i = 0; i < defaultn; ++i) { } sock = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP); //sock = WSASocket(AF_INET, SOCK_RAW, IPPROTO_ICMP, NULL, 0, WSA_FLAG_OVERLAPPED); if (sock == INVALID_SOCKET) { fprintf(stderr, "WSASocket() failed: %d\n", WSAGetLastError()); ExitProcess(STATUS_FAILED); } } else if (type == TCP) { fprintf(stdout, "\nPinging %s:%d....\n\n", inet_ntoa(sockaddrDest.sin_addr), ntohs(sockaddrDest.sin_port)); int failCounts = 0, failCountsRate = 0; for (int i = 0; i < defaultn; ++i) { sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); // WSA_FLAG_OVERLAPPED //sock = WSASocket(AF_INET, SOCK_STREAM, IPPROTO_TCP, NULL, 0, 0); if (sock == INVALID_SOCKET) { fprintf(stderr, "WSASocket() failed: %d\n", WSAGetLastError()); ExitProcess(STATUS_FAILED); } struct timeval tv; gettimeofday(&tv, NULL); starttime = tv.tv_sec * 1000 + tv.tv_usec / 1000; if (SOCKET_ERROR == connect(sock, (sockaddr*)&sockaddrDest, sockaddrDestlen)) { //may be connect timeout fprintf(stderr, "connect() failed: %d\n", WSAGetLastError()); ++failCounts; } gettimeofday(&tv, NULL); endtime = tv.tv_sec * 1000 + tv.tv_usec / 1000; printf("Connecting to %s: %dms\n", inet_ntoa(sockaddrDest.sin_addr), endtime - starttime); closesocket(sock); Sleep(defaulti * 1000); } if (defaultn != 0) { failCountsRate = failCounts * 100 / defaultn; } fprintf(stdout, "\n"); fprintf(stdout, "Tcp connect for %s:\n", inet_ntoa(sockaddrDest.sin_addr)); fprintf(stdout, " Sent = %d, Received = %d, Lost = %d (%d%% loss),\n", defaultn, defaultn - failCounts , failCounts, failCountsRate); } if (type == RAW){ //設置socket狀態 //設置收發時限 int timeout = 10; int bread; bread = setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, (char*)&timeout, sizeof(timeout)); if (bread == SOCKET_ERROR) { fprintf(stderr, "failed to set recv timeout: %d\n", WSAGetLastError()); ExitProcess(STATUS_FAILED); } bread = setsockopt(sock, SOL_SOCKET, SO_SNDTIMEO, (char*)&timeout, sizeof(timeout)); if (bread == SOCKET_ERROR) { fprintf(stderr, "failed to set send timeout: %d\n", WSAGetLastError()); ExitProcess(STATUS_FAILED); } ////設置socket緩沖區 //int nBuf = 32;//32bytes //bread = setsockopt(sock, SOL_SOCKET, SO_RCVBUF, (char*)&nBuf, sizeof(nBuf)); //if (bread == SOCKET_ERROR) { // fprintf(stderr, "failed to set recv timeout: %d\n", WSAGetLastError()); // ExitProcess(STATUS_FAILED); //} //bread = setsockopt(sock, SOL_SOCKET, SO_SNDBUF, (char*)&nBuf, sizeof(nBuf)); //if (bread == SOCKET_ERROR) { // fprintf(stderr, "failed to set send timeout: %d\n", WSAGetLastError()); // ExitProcess(STATUS_FAILED); //} fprintf(stdout, "\nPinging %s....\n\n", inet_ntoa(sockaddrDest.sin_addr)); //填充icmp報文 char *icmp_data; int datasize; icmp_data = (char*)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, (MAX_PACKET)); if (!icmp_data) { ExitProcess(STATUS_FAILED); } memset(icmp_data, ‘\0‘, MAX_PACKET); datasize = DEF_PACKET_SIZE; datasize += sizeof(IcmpHeader); static int count = 0; char *recvbuf; int nread; recvbuf = (char*)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, (MAX_PACKET)); int failCounts = 0, failCountsRate = 0; for (int i = 0; i < defaultn; ++i) { fillIcmpData(icmp_data, datasize); ((IcmpHeader*)icmp_data)->cksum = checkSum((USHORT*)icmp_data, datasize); //icmp校驗位 if (SOCKET_ERROR == sendto(sock, icmp_data, datasize, 0, (struct sockaddr*)&sockaddrDest, sizeof(sockaddrDest))) { printf("sendto failed: %d \n",WSAGetLastError()); ExitProcess(STATUS_FAILED); } if (SOCKET_ERROR == (nread = recvfrom(sock, recvbuf, MAX_PACKET, 0, (struct sockaddr*)&sockaddrDest, &sockaddrDestlen))) { if (WSAGetLastError() == WSAETIMEDOUT) { printf("time out\n"); continue; } printf("recvfrom failed: %d \n", WSAGetLastError()); ExitProcess(STATUS_FAILED); } decodeIcmpResp(recvbuf, nread, &sockaddrDest); Sleep(defaulti * 1000); } if (defaultn != 0) { failCountsRate = failCounts * 100 / defaultn; } fprintf(stdout, "\n"); fprintf(stdout, "Ping for %s:\n", inet_ntoa(sockaddrDest.sin_addr)); fprintf(stdout, " Sent = %d, Received = %d, Lost = %d (%d%% loss),\n", defaultn, defaultn - failCounts , failCounts, failCountsRate); } WSACleanup(); system("pause"); return 0; }View Code
windows平臺Ping的簡單實現