Ubuntu C语言实现ping

Ubuntu C语言实现ping

// ping.c
/*
 * @Author: D-lyw
 * @Date: 2018-11-01 17:00:20
 * @Last Modified by: D-lyw
 * @Last Modified time: 2018-12-01 17:01:42
 * @Description 在Linux环境利用socket编程,基于ICMP协议实现ping功能
 */
// #include "ping.h"
#include <stdio.h>
#include <string.h>
#include <netdb.h>            // struct icpmhdr, struct iphdr , gethostbyname, hostent
#include <arpa/inet.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#include <netinet/ip_icmp.h>
#include <sys/time.h>

#define E_FAILD_FD -1
#define ICMP_DATA_LEN 20  /* ICMP负载长度 */
#define ICMP_ECHO_MAX 4  /* ECHO-REQUEST报文发送次数 */
#define ICMP_REQUEST_TIMEOUT 2  /* 超时时间间隔 */

/* ICMP报文发送与接收缓存 */
static unsigned char aucSendBuf[1024 * 1024] = {0};
static unsigned char aucRecvBuf[1024 * 1024] = {0};

/* 结构体定义 */
typedef struct tagIcmpStatic
{
    unsigned int uiSendPktNum;
    unsigned int uiRcvPktNum;
    float fMinTime;
    float fMaxTime;
    float fArgTime;
}ICMP_STATIC_S;

/* 全局数据结构 */
ICMP_STATIC_S g_stPktStatic;      /* ICMP报文统计 */
struct timeval stSendTime = {0};  /* ECHO-REQUEST报文发送时间 */
struct timeval stRcvTime ={0};    /* ECHO-REPLY报文接收时间 */

/* 输出报文统计信息 */
void showStatic(const ICMP_STATIC_S *pstStInfo)
{
    unsigned int uiSend, uiRecv;

    uiSend = pstStInfo->uiSendPktNum;
    uiRecv = pstStInfo->uiRcvPktNum;
    printf("\n***PING Statistics***");
    printf("\nPackets:Send = %u,Recveived = %u,Lost = %u", uiSend, uiRecv, uiSend - uiRecv);
    printf("\nTime:Minimum = %.1fms,Maximum = %.1fms,Average=%.2fms\n", pstStInfo->fMinTime, pstStInfo->fMaxTime, pstStInfo->fArgTime);
}

/* 计算时间差,返回时间以毫秒为单位 */
unsigned int timeSub(const struct timeval *pstOut, const struct timeval *pstIn)
{
    unsigned int uiSec = 0;
    int iUsec = 0;

    uiSec = pstOut->tv_sec - pstIn->tv_sec;
    iUsec = pstOut->tv_usec - pstIn->tv_usec;
    if (0 > iUsec)
    {
        iUsec += 1000000;
        uiSec--;
    }
    return uiSec * 1000 + (unsigned int)(iUsec / 1000);
}

/* 校验和计算 */
unsigned short calcIcmpChkSum(const void *pPacket, int iPktLen)
{
    unsigned short usChkSum = 0;
    unsigned short *pusOffset = NULL;

    pusOffset = (unsigned short *)pPacket;
    while(1 < iPktLen)
    {
        usChkSum += *pusOffset++;
        iPktLen -= sizeof(unsigned short);
    }
    if (1 == iPktLen)
    {
        usChkSum += *((char *)pusOffset);
    }
    usChkSum = (usChkSum >> 16) + (usChkSum & 0xffff);
    usChkSum += (usChkSum >>16);

    return ~usChkSum;
}

/* ICMP报文填充 */
int newIcmpEcho(const int iPacketNum, const int iDataLen)
{
    struct icmp *pstIcmp = NULL;

    memset(aucSendBuf, 0, sizeof(aucSendBuf));
    pstIcmp = (struct icmp *)aucSendBuf;
    pstIcmp->icmp_type = ICMP_ECHO;
    pstIcmp->icmp_code = 0;
    pstIcmp->icmp_seq = htons((unsigned short)iPacketNum);
    pstIcmp->icmp_id = htons((unsigned short)getpid());
    pstIcmp->icmp_cksum = 0;
    pstIcmp->icmp_cksum = calcIcmpChkSum(pstIcmp, iDataLen + 8);
    return iDataLen + 8;
}

/* 解析ECHO-REPLY响应报文 */
int parseIcmp(const struct sockaddr_in *pstFromAddr, char *pRecvBuf, const int iLen)
{
    int iIpHeadLen = 0;
    int iIcmpLen = 0;
    struct ip *pstIp = NULL;
    struct icmp *pstIcmpReply = NULL;

    pstIp = (struct ip *)pRecvBuf;
    iIpHeadLen = pstIp->ip_hl << 2;
    pstIcmpReply = (struct icmp *)(pRecvBuf + iIpHeadLen);
    /* 报文长度非法 */
    iIcmpLen = iLen - iIpHeadLen;
    if (8 > iIcmpLen)
    {
        printf("[Error]Bad ICMP Echo-reply\n");
        return -1;
    }
    /* 报文类型非法 */
    if ((pstIcmpReply->icmp_type != ICMP_ECHOREPLY) ||
            (pstIcmpReply->icmp_id != htons((unsigned short)getpid())))
    {
        return -1;
    }
    sleep(1);
    printf("%d bytes reply from %s: icmp_seq=%u Time=%dms TTL=%d\n", iIcmpLen, inet_ntoa(pstFromAddr->sin_addr), ntohs(pstIcmpReply->icmp_seq), timeSub(&stRcvTime, &stSendTime), pstIp->ip_ttl);

    return 1;
}

/* Echo响应报文接收 */
void recvIcmp(const int fd)
{
    int iRet = 0;
    int iRecvLen = 0;
    unsigned int uiInterval = 0;
    socklen_t fromLen = sizeof(struct sockaddr_in);
    struct sockaddr_in stFromAddr = {0};

    /* 清空接收缓存,并准备接收响应报文 */
    memset(aucRecvBuf, 0, 1024 * 1024);
    iRecvLen = recvfrom(fd, (void *)aucRecvBuf, sizeof(aucRecvBuf), 0, (struct sockaddr *)&stFromAddr,&fromLen);
    gettimeofday(&stRcvTime, NULL);
    if (0 > iRecvLen)
    {
        if (EAGAIN == errno)
        {
            /* 请求超时 */
            printf("Request time out.\n");
            g_stPktStatic.fMaxTime = ~0;
        }
        else
        {
            /* 接收数据包出错 */
            perror("[Error]ICMP Receive");
        }
        return;
    }
    /* 获取统计参数 */
    g_stPktStatic.uiRcvPktNum++;
    uiInterval = timeSub(&stRcvTime, &stSendTime);
    g_stPktStatic.fArgTime = (g_stPktStatic.fArgTime * (g_stPktStatic.uiSendPktNum - 1) + uiInterval)/g_stPktStatic.uiSendPktNum;
    if (uiInterval < g_stPktStatic.fMinTime)
    {
        g_stPktStatic.fMinTime = uiInterval;
    }
    if (uiInterval > g_stPktStatic.fMaxTime)
    {
        g_stPktStatic.fMaxTime = uiInterval;
    }
    /* 解析ICMP响应报文 */
    iRet = parseIcmp(&stFromAddr, (char *)aucRecvBuf, iRecvLen);
    if (0 > iRet)
    {
        return;
    }
}

/* 发送ICMP报文 */
void sendIcmp(const int fd, const struct sockaddr_in *pstDestAddr)
{
    unsigned char ucEchoNum = 0;
    int iPktLen = 0;
    int iRet = 0;

    while(ICMP_ECHO_MAX > ucEchoNum)
    {
        iPktLen = newIcmpEcho(ucEchoNum, ICMP_DATA_LEN);
        /* 记录发送起始时间 */
        g_stPktStatic.uiSendPktNum++;
        gettimeofday(&stSendTime, NULL);
        /* 发送ICMP-ECHO报文 */
        iRet = sendto(fd, aucSendBuf, iPktLen, 0, (struct sockaddr *)pstDestAddr, sizeof(struct sockaddr_in));
        if(0 > iRet)
        {
            perror("Send ICMP Error");
            continue;
        }
        /* 等待接收响应报文 */
        recvIcmp(fd);
        ucEchoNum++;
    }
}

/*主函数*/
int main(int argc, char *argv[])
{
    int iRet = 0;
    int iRcvBufSize = 1024 * 1024;
    int fd = E_FAILD_FD;
    in_addr_t stHostAddr;

    struct timeval stRcvTimeOut = {0};
    struct hostent  *pHost = NULL;
    struct sockaddr_in stDestAddr = {0};
    struct protoent *pProtoIcmp = NULL;
    g_stPktStatic.uiSendPktNum = 0;
    g_stPktStatic.uiRcvPktNum =0;
    g_stPktStatic.fMinTime = 1000000.0;
    g_stPktStatic.fMaxTime = -1.0;
    g_stPktStatic.fArgTime = 0.0;

    /* 参数判断 */
    if (2 > argc)
    {
        printf("\nUsage:%s hostname/IP address\n", argv[0]);
        return -1;
    }
    /* 获取ICMP协议类型  */
    pProtoIcmp = getprotobyname("icmp");
    if (NULL == pProtoIcmp)
    {
        perror("[Error]Get ICMP Protoent Structrue");
        return -1;
    }
    /* 创建ICMP使用的SOCKET */
    fd = socket(PF_INET, SOCK_RAW, pProtoIcmp->p_proto);
    if (0 > fd)
    {
        perror("[Error]Init Socket");
        return -1;
    }
    /* ROOT权限回收 */
    iRet = setuid(getuid());
    if (0 > iRet)
    {
        perror("[Error]Setuid");
        close(fd);
        return -1;
    }
    /* 设置SOCKET选项 */
    stRcvTimeOut.tv_sec = ICMP_REQUEST_TIMEOUT;
    setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &iRcvBufSize, sizeof(iRcvBufSize));
    setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, &stRcvTimeOut, sizeof(struct timeval));

    /* 地址解析 */
    stHostAddr = inet_addr(argv[1]);
    if (INADDR_NONE == stHostAddr)
    {
        /* 根据主机名称解析IP地址 */
        pHost = gethostbyname(argv[1]);
        if (NULL == pHost)
        {
            perror("[Error]Host Name Error");
            close(fd);
            return -1;
        }
        memcpy((char *)&stDestAddr.sin_addr, (char *)(pHost->h_addr), pHost->h_length);
    }
    else
    {
        memcpy((char *)&stDestAddr.sin_addr, (char *)&stHostAddr, sizeof(stHostAddr));
    }
    printf("\nPING %s(%s): %d bytes in ICMP packets\n", argv[1], inet_ntoa(stDestAddr.sin_addr), ICMP_DATA_LEN);
    /* 发送ICMP报文 */
    sendIcmp(fd, &stDestAddr);

    /* 输出统计信息 */
    showStatic(&g_stPktStatic);
    /* 关闭FD */
    close(fd);
    return 0;
}

在这里插入图片描述

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值