C语言实现ping命令

C语言实现ping命令

ping.h

#ifndef __PING_H__
#define __PING_H__


typedef void (*PFN_PING_CALLBACK)(void *cbctx, char *ip, int bytes, int ttl, int time, int seq);
void* ping_init(int interval, int timeout, PFN_PING_CALLBACK callback, void *cbctx);
void  ping_exit(void *ctx);
void  ping_run (void *ctx, char *ip, int start);
int   ping_isok(void *ctx);

#endif

ping.c

#include <stdint.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <pthread.h>
#include "ping.h"

#ifdef WIN32
#include <winsock2.h>
#define usleep(t) Sleep((t) / 1000)
#define get_tick_count GetTickCount
#define socklen_t int32_t
#else
#include <time.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#define SOCKET int
#define closesocket close
#define stricmp strcasecmp
#define strtok_s strtok_r
static uint32_t get_tick_count()
{
    struct timespec ts;
    clock_gettime(CLOCK_MONOTONIC, &ts);    // 获取从系统启动到现在的时间
    return (ts.tv_sec * 1000 + ts.tv_nsec / 1000000);
}
#endif

#pragma pack(1)  // 让编译器将结构体数据强制连续排列(1字节对齐)
typedef struct {
    uint8_t  type;
    uint8_t  code;
    uint16_t checksum;
    uint16_t id;
    uint16_t seq;
    uint32_t data;
} ICMPPKT;
#pragma pack()  // 恢复默认字节对齐方式

typedef struct {
    PFN_PING_CALLBACK  callback;
    void              *cbctxt;
    SOCKET             socket;
    struct sockaddr_in dstaddr;
    #define FLAG_EXIT  (1 << 0)
    #define FLAG_START (1 << 1)
    uint32_t           flags;
    uint32_t           ticksend;
    uint32_t           tickrecv;
    int32_t            interval;    // 间隔
    int32_t            timeout;     // 超时时间
    pthread_t          pthread;
    ICMPPKT            icmppkt;
    uint8_t            recvbuf[256];
} PING;

static uint16_t checksum(uint8_t *buf, int len)
{
    uint16_t *p16 = (uint16_t*)buf;
    uint32_t  sum = 0;

    while (len >= 2) {
        sum += *p16++;
        len -= 2;
    }
    if (len) sum += *(uint8_t*)p16;

    sum  = (sum >> 16) + (sum & 0xffff);
    sum += (sum >> 16);

    return (uint16_t)~sum;
}

static void* ping_thread_proc(void *argv)
{
    PING *ping = (PING*)argv;
    if (!argv) return NULL;
    while (!(ping->flags & FLAG_EXIT)) {
        if (ping->flags & FLAG_START) {
            struct sockaddr_in srcaddr;
            uint32_t  tickcur = get_tick_count();
            socklen_t addrlen = sizeof(srcaddr);
            int32_t   bytes;
            if (ping->ticksend == 0 || (int32_t)tickcur - (int32_t)ping->ticksend >= ping->interval) {
                ping->icmppkt.seq++;
                ping->icmppkt.checksum = 0;
                ping->icmppkt.type     = 8;
                ping->icmppkt.data     = tickcur;
                ping->icmppkt.id       = getpid();
                ping->icmppkt.checksum = checksum((uint8_t*)&ping->icmppkt, sizeof(ping->icmppkt));
                sendto(ping->socket, (char*)&ping->icmppkt, sizeof(ping->icmppkt), 0, (struct sockaddr*)&ping->dstaddr, sizeof(ping->dstaddr));
                ping->ticksend += ping->ticksend ? ping->interval : tickcur;
             printf("socket: %d, send icmp packet, seq: %d, checksum: %04X\n", ping->socket, ping->icmppkt.seq, ping->icmppkt.checksum); fflush(stdout);
            }
            bytes = recvfrom(ping->socket, (char*)ping->recvbuf, sizeof(ping->recvbuf), 0, (struct sockaddr*)&srcaddr, &addrlen);
            if (bytes > 0) {
                ICMPPKT   *pkt = (ICMPPKT*)(ping->recvbuf + 20);
                if (pkt->type == 0 && pkt->code == 0) {
                    ping->tickrecv = get_tick_count();
                    if (ping->callback) {
                        ping->callback(ping->cbctxt, inet_ntoa(srcaddr.sin_addr), bytes, ping->recvbuf[8], (int32_t)ping->tickrecv - (int32_t)pkt->data, pkt->seq);
                    }
                }
            }
        } else {
            ping->ticksend = 0;
            usleep(100 * 1000);
        }
    }
    return NULL;
}
/**
 * 间隔,超时时间
*/
void* ping_init(int interval, int timeout, PFN_PING_CALLBACK callback, void *cbctx)
{
#ifdef WIN32
    WSADATA wsaData;
    uint32_t opt = 100;
#else
    struct timeval tv = { 0, 100 * 1000 };
#endif
    PING *ping = NULL;

#ifdef WIN32
    if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) {
        printf("WSAStartup failed !\n");
        return NULL;
    }
#endif

    ping = calloc(1, sizeof(PING));
    if (!ping) return NULL;
    ping->interval = interval;
    ping->timeout  = timeout ;
    ping->callback = callback;
    ping->cbctxt   = cbctx;
    ping->socket   = socket(PF_INET, SOCK_RAW, IPPROTO_ICMP);
#ifdef WIN32
    setsockopt(ping->socket, SOL_SOCKET, SO_RCVTIMEO , (char*)&opt, sizeof(opt));
#else
    setsockopt(ping->socket, SOL_SOCKET, SO_RCVTIMEO , (char*)&tv , sizeof(tv )); // 接收超时
#endif
    pthread_create(&ping->pthread, NULL, ping_thread_proc, ping);
    return ping;
}

void ping_exit(void *ctx)
{
    PING *ping = (PING*)ctx;
    if (!ctx) return;
    ping->flags |= FLAG_EXIT;
    pthread_join(ping->pthread, NULL);
    if (ping->socket > 0) closesocket(ping->socket);
    free(ctx);
#ifdef WIN32
    WSACleanup();
#endif
}

void ping_run(void *ctx, char *ip, int start)
{
    PING *ping = (PING*)ctx;
    if (!ctx) return;
    if (ip) {
        ping->dstaddr.sin_family      = AF_INET;
        ping->dstaddr.sin_addr.s_addr = inet_addr(ip);
    }
    if (start) ping->flags |= FLAG_START;
    else       ping->flags &=~FLAG_START;
}

int ping_isok(void *ctx)
{
    PING *ping = (PING*)ctx;
    return (ping && ping->tickrecv && (int32_t)get_tick_count() - (int32_t)ping->tickrecv < ping->timeout);
}

//#ifdef _TEST_
static void ping_callback(void *cbctx, char *ip, int bytes, int ttl, int time, int seq)
{
    printf("reply from %s, bytes: %d, ttl: %dms, time: %dms, seq: %d\n", ip, bytes, ttl, time, seq); fflush(stdout);
}

int main(void)
{
    PING *ping = ping_init(1000, 2000, ping_callback, NULL);
    while (1) {
        puts("input: ping stop isok exit");
        char cmd[256], ip[256];
        scanf("%256s", cmd);
        if (strcmp(cmd, "ping") == 0) {
            puts("input ip");
            scanf("%256s", ip);
            printf("ip = %s\n", ip);
            ping_run(ping, ip, 1);
        } else if (strcmp(cmd, "stop") == 0) {
            ping_run(ping, NULL, 0);
        } else if (strcmp(cmd, "isok") == 0) {
            printf("%d\n", ping_isok(ping)); fflush(stdout);
        } else if (strcmp(cmd, "exit") == 0 || strcmp(cmd, "quit") == 0) {
            break;
        }
    }
    ping_exit(ping);
    return 0;
}
//#endif

build.sh

#!/bin/bash
gcc -Wall -D_TEST_ ping.c -o ping #-lws2_32
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

光头,强

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值