1. 程式人生 > >windows平臺Ping的簡單實現

windows平臺Ping的簡單實現

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
#define
ICMP_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 short
ident; // 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*);
View Code

//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的簡單實現