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*);

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

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

原文地址:https://www.cnblogs.com/dandancheng/p/8075269.html

时间: 2024-08-30 15:24:28

windows平台Ping的简单实现的相关文章

最简单的windows平台Git服务器---Gitstack 【转】

转自:http://www.360doc.com/content/12/0503/11/1016783_208316518.shtml 目前在windows平台上的git服务器大多数采用CopSSH+MsysGit的方式来实现,当然这种方式也是最原汁原味的在windows平台上搭建git服务器的方式,提供了最高程度的安全性和灵活性.但是缺点在于搭建仍然比较麻烦,要是能有一个Git版本的类似VisualSVN的服务器搭建包相信会更有利于git在windows平台上的使用.我在这里推荐一个目前来说部

Windows平台分布式架构实践 - 负载均衡

概述 最近.NET的世界开始闹腾了,微软官方终于加入到了对.NET跨平台的支持,并且在不久的将来,我们在VS里面写的代码可能就可以通过 Mono直接在Linux和Mac上运行.那么大家(开发者和企业)为什么那么的迫切的希望.NET跨平台呢?第一个理由是便宜,淘宝号称4万多台服务器 全部运行在Linux,Linux平台下还有免费的MySql,这些都是免费的,这些省下来直接就是利润呀,做企业的成本可以降低又没有任何损失,何乐而 不为呢?第二个理由是在Linux系统下还有很多非常优秀的构架(当然同样也

QT5.x应用在Mac OS X和Windows平台的发布过程

QT是一款非常牛逼的跨平台开发工具,目前可以开发Mac OS X.Windows.Linux.Android.iOS等平台的App.对于Android和iOS平台,发布相对容易,例如,Android平台是生成apk文件上传到Android设备的,所以直接安装apk文件即可.不过对于PC平台(Mac OS X.Windows和Linux)的应用,在发布时就显得麻烦些. 本文主要介绍Mac OS X和Windows平台的发布过程.这两个平台运行的应用都称为可执行程序.Windows平台可执行文件扩展

windows平台python 2.7环境编译安装zbar

最近一个项目需要识别二维码,找来找去找到了zbar和zxing,中间越过无数坑,总算基本上弄明白,分享出来给大家. 一.zbar官方介绍 ZBar 是款桌面电脑用条形码/二维码扫描工具,支持摄像头及图片扫描,支持多平台,例如 iPhone,Andriod 手机,同时 ZBar封装了二维码扫描的 API 开发包. ZBar 目前条码类型有:EAN-13/UPC-A, UPC-E, EAN-8, Code 128, Code 39, Interleaved 2 of 5 and QR Code. 从

Windows平台下利用APM来做负载均衡方案 - 负载均衡(下)

概述 我们在上一篇Windows平台分布式架构实践 - 负载均衡中讨论了Windows平台下通过NLB(Network Load Balancer) 来实现网站的负载均衡,并且通过压力测试演示了它的效果,可以说还是非常的理想的.同时我们也收集到了不少的问题,比如说如何在这种分布式的架构下使用Session,NLB中有一台服务器挂掉了会导致对外暴露的地址无法访问,如果实现服务器之间的同步,如果更好的进行热修复等等,还有我们在上一篇中也提到了NLB所提供的功能是非常简单的,为了回答我们前面提到的问题

基于科大讯飞语音云windows平台开发

前记: 前段时间公司没事干,突发奇想想做一个语音识别系统,看起来应该很简单的,但做起来却是各种问题,这个对电气毕业的我,却是挺为难的.谷姐已经离我们而去,感谢度娘,感谢CSDN各位大神,好歹也做的是那么回事了,虽然还是不好用,但基本功能实现了. 该软件使用VS2008C++/CLR开发,由于科大讯飞提供的是C的API接口,结果到这边就是各种不兼容,CLR是基于托管堆运行的,而这个API有是非托管堆的,使用了各种指针,原本打算使用C#来做,最后门外汉的我也没能做到C#和C指针完美结合,真怀恋单片机

windows平台下基于VisualStudio的Clang安装和配置

LLVM 是一个开源的编译器架构,它已经被成功应用到多个应用领域.Clang是 LLVM 的一个编译器前端,它目前支持 C, C++, Objective-C 以及 Objective-C++ 等编程语言.Clang 对源程序进行词法分析和语义分析,并将分析结果转换为 AST ( 抽象语法树 ) ,最后使用 LLVM 作为后端代码的生成器. Clang 的开发目标是提供一个可以替代 GCC 的前端编译器.与 GCC 相比,Clang 是一个重新设计的编译器前端,具有一系列优点,例如模块化,代码简

windows平台下安装与配置mysql5.7

博主QQ:819594300 博客地址:http://zpf666.blog.51cto.com/ 有什么疑问的朋友可以联系博主,博主会帮你们解答,谢谢支持! 在windows上安装mysql5.7需要具有系统的管理员权限. Windows平台下提供两种安装方式: 1.mysql二进制分发版(.msi安装文件) 2.免安装版(.zip压缩文件) 一般来讲,我们使用二进制分发版,因为该版本比其他的分发版本使用起来要简单,不再需要其他工具来启动就可以运行mysql. 本例以window7平台为例进行

Linux下编译静态MinGW环境,编译windows平台Qt程序(使用MXE)

参考链接: MXE.>大多数程序都是在windows平台下开发的程序.windows 在现实中也是绕不过的一个系统平台,做为受过几年VC,MFC”虐待”的程序员,在做为一个程序员之前是一位Linux重度使用者,受够了MFC之后一直想要找一个框架替换,使用过GTK,wxWidgets,Qt,最后还是Qt用得多一些.我认为程序跨平台应该是一个基本标准,同一份代码不需改动,或者改动极少,放在不同的平台下编译就能使用.不同平台,同样的界面,同样的操作,同样的体验.这里要讲的是我如何在Linux 下开发跨