windows平台Ping的简单实现

//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

 

posted @ 2017-12-25 18:00  dandancheng  阅读(348)  评论(0编辑  收藏  举报