现在的位置: 首页 > 自动控制 > 工业·编程 > 正文

Ping程序C++实现

2012-08-21 06:07 工业·编程 ⁄ 共 8411字 ⁄ 字号 暂无评论

学习内容,参见《Windows网络编程》第13章原始套接字

Ping程序实现步骤

  1. 创建类型为SOCK_RAW的一个套接字,同时设定协议IPPROTO_ICMP。
  2. 创建并初始化ICMP头。
  3. 调用sendto或WSASendto,将ICMP请求发给远程主机。
  4. 调用recvfrom或WSARecvfrom,以接收任何ICMP响应。

ICMP简介

ICMP是(Internet ControlMessage Protocol)Internet控制报文协议。用于在IP主机、路由器之间传递控制消息。控制消息是指网络通不通、主机是否可达、路由是否可用等网络本身的消息。

系统自带ping程序,检测百度是否可访问。

clip_image001

自己实现ping程序,检测百度是否可访问。

要显示访问IP地址,该IP即为系统自带ping程序解析IP,如上图为119.75.218.77。

clip_image002

使用windowsWinsock 2编程,需要进行工程配置。

  • 工程右键Properties->ConfigurationProperties->Linker->Input->Additional Dependencies中添加ws2_32.lib。
  • Demo代码采用Multi-Byte方式,设置Properties->ConfigurationProperties->General->Character Set为Use Multi-Byte Character Set。

备注:所有关系到收发数据的缓冲都属于简单的char类型,这些函数没有Unicode版本。当字符集为Unicode时,需要进行字符串转换。

程序源码,MyPing.cpp。

// MyPing.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
#include <winsock2.h>
#include <WS2tcpip.h>
#include <stdio.h>
#include <stdlib.h>

#define ICMP_ECHO 8
#define ICMP_ECHOREPLY 0
#define ICMP_MIN 8 //Minimum 8-byte ICMP packet (header)

#define DEF_PACKET_SIZE 32
#define MAX_PACKET 1024
#define MAX_IP_HDR_SIZE 60

//IP header structure
typedef struct _iphdr
{
    unsigned int h_len:4;//Length of the header
    unsigned int version:4;//Version of IP
    unsigned char tos;//Type of service
    unsigned short total_len;//Total length of the packet
    unsigned short ident;//Unique identifier
    unsigned short frag_and_flags;//Flags
    unsigned char ttl;//Time to live
    unsigned char proto;//Protocol (TCP,UDP,etc.)
    unsigned short checksum;//IP checksum

    unsigned int sourceIP;
    unsigned int destIP;
} IpHeader;

//ICMP header structure
typedef struct _icmphdr
{
    BYTE i_type;
    BYTE i_code;//Type sub code
    USHORT i_cksum;
    USHORT i_id;
    USHORT i_seq;

    //This is not the standard header, but we reserve space for time
    ULONG timestamp;
} IcmpHeader;

//IP option header--use with socket option IP_OPTIONS
typedef struct _ipoptionhdr
{
    unsigned char code;//Option type
    unsigned char len;//Length of option hdr
    unsigned char ptr;//Offset into optons
    unsigned long addr[9];//List of IP addrs
} IpOptionHeader;

int datasize;
char* lpdest;

//Print usage information
void usage()
{
    printf("usage:MyPing -i:IP [data size]\n");
    printf("    -i:IP           remote machine to Ping\n");
    printf("    datasize     can be up to 1 KB\n");

    ExitProcess(1);
}

//Helper function to fill in various fields for our ICMP request
void FillICMPData(char* icmp_data, int datasize)
{
    IcmpHeader* icmp_hdr = (IcmpHeader*)icmp_data;
    icmp_hdr->i_type = ICMP_ECHO;//Request an ICMP echo
    icmp_hdr->i_code = 0;
    icmp_hdr->i_id = (USHORT)GetCurrentProcessId();
    icmp_hdr->i_cksum = 0;
    icmp_hdr->i_seq = 0;

    char* datapart = icmp_data + sizeof(IcmpHeader);

    //Place some junk in the buffer
    memset(datapart, 'E', datasize - sizeof(IcmpHeader));
}

//This function calculates the 16-bit one's complement sum
//of the supplied buffer (ICMP) header
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);
}

//If the IP option header is present, find the IP options
//within the IP header and print the record route option values
void DecodeIPOptions(char* buf, int bytes)
{
    IpOptionHeader* ipopt = (IpOptionHeader*)(buf + 20);

    printf("RR:    ");
    for (int i = 0; i < (ipopt->ptr / 4) - 1; i++)
    {
        IN_ADDR inaddr;
        inaddr.S_un.S_addr = ipopt->addr[i];

        if (i != 0)
        {
            printf("        ");
        }

        HOSTENT* host = gethostbyaddr((char*)&inaddr.S_un.S_addr,
            sizeof(inaddr.S_un.S_addr), AF_INET);
        if (host)
        {
            printf("(%-15s) %s\n", inet_ntoa(inaddr), host->h_name);
        }
        else
        {
            printf("(%-15s)\n", inet_ntoa(inaddr));
        }
    }

    return;
}

//The response is an IP packet. We must decode the IP header to
//locate the ICMP data.
void DecodeICMPHeader(char* buf, int bytes, struct sockaddr_in* from)
{
    static int icmpcount = 0;
    IpHeader* iphdr = (IpHeader*)buf;

    //Number of 32-bit words * 4 = bytes
    unsigned short iphdrlen = iphdr->h_len * 4;
    DWORD tick = GetTickCount();

    if ((iphdrlen == MAX_IP_HDR_SIZE) && (!icmpcount))
    {
        DecodeIPOptions(buf, bytes);
    }

    if (bytes < iphdrlen + ICMP_MIN)
    {
        printf("Too few bytes from %s\n", inet_ntoa(from->sin_addr));
    }

    IcmpHeader* icmphdr = (IcmpHeader*)(buf + iphdrlen);
    if (icmphdr->i_type != ICMP_ECHOREPLY)
    {
        printf("nonecho type %d recvd\n", icmphdr->i_type);
        return;
    }

    //Make sure this is an ICMP reply to something we sent!
    if (icmphdr->i_id != (USHORT)GetCurrentProcessId())
    {
        printf("someone else's packet!\n");
        return;
    }

    printf("%d bytes from %s:", bytes, inet_ntoa(from->sin_addr));
    printf("  icmp_seq = %d. ", icmphdr->i_seq);
    printf("  time:%d ms", tick - icmphdr->timestamp);
    printf("\n");

    icmpcount++;
    return;
}

void ValidateArgs(int argc, _TCHAR** argv)
{
    lpdest = NULL;
    datasize = DEF_PACKET_SIZE;

    for (int i = 1; i < argc; i++)
    {
        if ((argv[i][0] == '-') || (argv[i][0] == '/'))
        {
            switch (tolower(argv[i][1]))
            {
            case 'i':
                lpdest = argv[i] + 3;
                break;
            default:
                usage();
                break;
            }
        }
        else if (isdigit(argv[i][0]))
        {
            datasize = atoi(argv[i]);
        }
    }
}

int _tmain(int argc, _TCHAR* argv[])
{
    WSADATA wsaData;
    if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0)
    {
        printf("WSAStartup() failed:%d\n", GetLastError());
        return -1;
    }

    ValidateArgs(argc, argv);

    SOCKET sockRaw = WSASocket(AF_INET, SOCK_RAW, IPPROTO_ICMP,
        NULL, 0, WSA_FLAG_OVERLAPPED);
    if (sockRaw == INVALID_SOCKET)
    {
        printf("WSASocket() failed:%d\n", WSAGetLastError());
        return -1;
    }

    //Set the send/recv timeout values
    struct sockaddr_in from;
    int fromlen = sizeof(from);
    int timeout = 1000;

    int bread = setsockopt(sockRaw, SOL_SOCKET, SO_RCVTIMEO,
        (char*)&timeout, sizeof(timeout));
    if (bread == SOCKET_ERROR)
    {
        printf("setsockopt(SO_RCVTIMEO) failed:%d\n", WSAGetLastError());
        return -1;
    }

    timeout = 1000;
    bread = setsockopt(sockRaw, SOL_SOCKET, SO_SNDTIMEO,
        (char*)&timeout, sizeof(timeout));
    if (bread == SOCKET_ERROR)
    {
        printf("setsockopt(SO_SNDTIMEO) failed:%d\n", WSAGetLastError());
        return -1;
    }

    struct sockaddr_in dest;
    memset(&dest, 0, sizeof(dest));

    //Resolve the endpoint's name if necessary
    dest.sin_family = AF_INET;

    if ((lpdest != NULL) && strlen(lpdest) != 0)
    {
        dest.sin_addr.s_addr = inet_addr(lpdest);
    }
    else
    {
        struct hostent* hp = gethostbyname(lpdest);
        if (hp != NULL)
        {
            memcpy(&(dest.sin_addr), hp->h_addr, hp->h_length);
            dest.sin_family = hp->h_addrtype;
            printf("dest.sin_addr = %s\n", inet_ntoa(dest.sin_addr));
        }
        else
        {
            printf("gethostbyname() failed:%d\n", WSAGetLastError());
            return -1;
        }
    }

    //Create the ICMP packet
    datasize += sizeof(IcmpHeader);

    char* icmp_data = (char*)HeapAlloc(GetProcessHeap(),
        HEAP_ZERO_MEMORY, MAX_PACKET);
    if (!icmp_data)
    {
        printf("HeapAlloc() failed:%d\n", GetLastError());
        return -1;
    }
    memset(icmp_data, 0, MAX_PACKET);
    FillICMPData(icmp_data, datasize);

    char* recvbuf = (char*)HeapAlloc(GetProcessHeap(),
        HEAP_ZERO_MEMORY, MAX_PACKET);

    //Start sending/receiving ICMP packets
    USHORT seq_no = 0;
    while (true)
    {
        static int nCount = 0;
        int bwrote;

        if (nCount++ == 4)
        {
            break;
        }

        ((IcmpHeader*)icmp_data)->i_cksum = 0;
        ((IcmpHeader*)icmp_data)->timestamp = GetTickCount();
        ((IcmpHeader*)icmp_data)->i_seq = seq_no++;
        ((IcmpHeader*)icmp_data)->i_cksum =
            checksum((USHORT*)icmp_data, datasize);

        bwrote = sendto(sockRaw, icmp_data, datasize, 0,
            (struct sockaddr*)&dest, sizeof(dest));
        if (bwrote == SOCKET_ERROR)
        {
            if (WSAGetLastError() == WSAETIMEDOUT)
            {
                printf("timed out\n");
                continue;
            }

            printf("sendto() failed:%d\n", WSAGetLastError());
            return -1;
        }

        if (bwrote < datasize)
        {
            printf("Wrote %d bytes\n", bwrote);
        }

        bread = recvfrom(sockRaw, recvbuf, MAX_PACKET, 0,
            (struct sockaddr*)&from, &fromlen);
        if (bread == SOCKET_ERROR)
        {
            if (WSAGetLastError() == WSAETIMEDOUT)
            {
                printf("timed out\n");
                continue;
            }

            printf("revefrom() failed:%d\n", WSAGetLastError());
            return -1;
        }

        DecodeICMPHeader(recvbuf, bread, &from);

        Sleep(1000);
    }

    //Cleanup
    if (sockRaw != INVALID_SOCKET)
    {
        closesocket(sockRaw);
    }

    HeapFree(GetProcessHeap(), 0, recvbuf);
    HeapFree(GetProcessHeap(), 0, icmp_data);

    WSACleanup();

    return 0;
}

给我留言

留言无头像?