Ping 的C语言实现

转载自 http://blog.csdn.net/wangjiannuaa/article/details/6269124

// Ping.c
//     Ping [host] [packet-size]
//
//     host         String name of host to ping
//     packet-size  Integer size of packet to send
//                      (smaller than 1024 bytes)
#define WIN32_LEAN_AND_MEAN
#include <winsock2.h>
#include <ws2tcpip.h>
#include <stdio.h>
#include <stdlib.h>
#pragma comment(lib,"ws2_32.lib")
#define IP_RECORD_ROUTE  0x7
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;
#define ICMP_ECHO        8
#define ICMP_ECHOREPLY   0
#define ICMP_MIN         8 // Minimum 8-byte ICMP packet (header)
typedef struct _icmphdr {
    BYTE   i_type;
    BYTE   i_code;                 // Type sub code
    USHORT i_cksum;
    USHORT i_id;
    USHORT i_seq;
    ULONG  timestamp;
} IcmpHeader;
typedef struct _ipoptionhdr {
    unsigned char        code;        // Option type
    unsigned char        len;         // Length of option hdr
    unsigned char        ptr;         // Offset into options
    unsigned long        addr[9];     // List of IP addrs
} IpOptionHeader;
#define DEF_PACKET_SIZE  32        // Default packet size
#define MAX_PACKET       1024      // Max ICMP packet size
#define MAX_IP_HDR_SIZE  60        // Max IP header size w/options
BOOL  bRecordRoute;
int   datasize;
char *lpdest;
void usage(char *progname) 
{
    printf("usage: ping -r <host> [data size]/n");
    printf("       -r           record route/n");
    printf("        host        remote machine to ping/n");
    printf("        datasize    can be up to 1KB/n");
    ExitProcess(-1);
}
void FillICMPData(char *icmp_data, int datasize) 
{
    IcmpHeader *icmp_hdr = NULL;
    char *datapart = NULL;
    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;
    datapart = icmp_data + sizeof(IcmpHeader);
    //
    // Place some junk in the buffer
    //
    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 DecodeIPOptions(char *buf, int bytes) 
{
    IpOptionHeader *ipopt = NULL;
    IN_ADDR inaddr;
    int i;
    HOSTENT *host = NULL;
    ipopt = (IpOptionHeader *)(buf + 20);
    printf("RR:   ");
    for (i = 0; i < (ipopt->ptr / 4) - 1; i++) {
        inaddr.S_un.S_addr = ipopt->addr[i];
        if (i != 0)
            printf("      ");
        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;
}
void DecodeICMPHeader(char *buf, int bytes, struct sockaddr_in *from) 
{
    IpHeader       *iphdr = NULL;
    IcmpHeader     *icmphdr = NULL;
    unsigned short  iphdrlen;
    DWORD           tick;
    static   int    icmpcount = 0;
    iphdr = (IpHeader *)buf;
    // Number of 32-bit words * 4 = bytes
    iphdrlen = iphdr->h_len * 4;
    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));
    }
    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: %lu ms", tick - icmphdr->timestamp);
    printf("/n");
    icmpcount++;
    return;
}
void ValidateArgs(int argc, char **argv) 
{
    int     i;
    bRecordRoute = FALSE;
    lpdest = NULL;
    datasize = DEF_PACKET_SIZE;
    for (i = 1; i < argc; i++) {
        if ((argv[i][0] == '-') || (argv[i][0] == '/')) {
            switch (tolower(argv[i][1])) {
            case 'r':        // Record route option
                bRecordRoute = TRUE;
                break;
            default:
                usage(argv[0]);
                break;
            }
        } else if (isdigit(argv[i][0]))
            datasize = atoi(argv[i]);
        else
            lpdest = argv[i];
    }
}
int main(int argc, char **argv) 
{
    WSADATA wsaData;
    SOCKET sockRaw = INVALID_SOCKET;
    struct sockaddr_in dest;
    struct sockaddr_in from;
    int bread;
    int fromlen = sizeof(from);
    int timeout = 1000;
    int ret;
    char *icmp_data = NULL;
    char *recvbuf = NULL;
    USHORT seq_no = 0;
    struct hostent *hp = NULL;
    IpOptionHeader ipopt;
    if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) {
        printf("WSAStartup() failed: %ld/n", GetLastError());
        return -1;
    }
    ValidateArgs(argc, argv);
    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;
    }
    if (bRecordRoute) {
        // Setup the IP option header to go out on every ICMP packet
        //
        ZeroMemory(&ipopt, sizeof(ipopt));
        ipopt.code = IP_RECORD_ROUTE; // Record route option
        ipopt.ptr  = 4;               // Point to the first addr offset
        ipopt.len  = 39;              // Length of option header
        ret = setsockopt(sockRaw, IPPROTO_IP, IP_OPTIONS,
                         (char *)&ipopt, sizeof(ipopt));
        if (ret == SOCKET_ERROR) {
            printf("setsockopt(IP_OPTIONS) failed: %d/n",
                   WSAGetLastError());
        }
    }
    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;
    }
    memset(&dest, 0, sizeof(dest));
    dest.sin_family = AF_INET;
    if ((dest.sin_addr.s_addr = inet_addr(lpdest)) == INADDR_NONE) {
        if ((hp = gethostbyname(lpdest)) != 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;
        }
    }
    datasize += sizeof(IcmpHeader);
    icmp_data = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
                          MAX_PACKET);
    recvbuf = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
                        MAX_PACKET);
    if (!icmp_data) {
        printf("HeapAlloc() failed: %ld/n", GetLastError());
        return -1;
    }
    memset(icmp_data,0,MAX_PACKET);
    FillICMPData(icmp_data,datasize);
    while (1) {
        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("recvfrom() failed: %d/n", WSAGetLastError());
            return -1;
        }
        DecodeICMPHeader(recvbuf, bread, &from);
        Sleep(1000);
    }
    if (sockRaw != INVALID_SOCKET)
        closesocket(sockRaw);
    HeapFree(GetProcessHeap(), 0, recvbuf);
    HeapFree(GetProcessHeap(), 0, icmp_data);
    WSACleanup();
    return 0;
}

转载于:https://www.cnblogs.com/Hashmat/archive/2013/05/09/3069567.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Ping命令是一种网络诊断工具,可以检查网络主机是否可达,以及检测主机之间的网络延迟。为了实现Ping命令,我们需要使用C语言编写一个程序,该程序利用Internet控制消息协议(ICMP)协议发送数据包,并接收目标主机返回的响应,以计算网络延迟。 要使用C语言实现Ping命令,需要遵循以下步骤: 1. 创建原始套接字 使用socket()函数创建原始套接字。原始套接字可以直接使用ICMP协议进行通信。 2. 填写ICMP协议头 ICMP协议头中包含了一些必要的信息,例如ICMP类型、ICMP编码和校验和等。 3. 填写数据部分 Ping命令通常会发送一些生产数据,例如时间戳或序列号。 4. 计算校验和 在ICMP协议头填写完毕后,需要计算校验和。校验和用于检验数据在传输过程中是否被篡改。 5. 发送请求 使用sendto()函数发送ICMP数据包,以请求目标主机进行响应。 6. 接收响应 使用recvfrom()函数接收目标主机的响应数据包。 7. 解析响应 根据ICMP协议头中的类型和编码信息,判断响应是来自目标主机的响应,还是一些异常情况的提示信息。如果是目标主机的响应,则从数据部分解析出需要的信息(例如网络延迟),并打印在控制台上。 以上就是使用C语言实现Ping命令的步骤。需要注意的是,由于Ping命令需要使用超级用户权限运行,因此我们需要将编写好的程序设置为setuid程序,以获取高级别用户权限。同时,由于Ping命令是一个常见的网络诊断工具,因此我们还需要对程序进行性能和安全性优化,以便更好地服务于用户。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值