Ping code 实现分享

   很久之前参阅文档写了ping实现,当时不甚在意,没有留档,导致后续需要的时候又得费力,发此code,一是留档,而是共勉,能为他人提供方便,将不甚荣幸,结构或有不够严谨,但作为ping测试却是可以了,报文结构网述颇多,这里就不再详述了

    该code中icmp报文结构为自组装,适合轻量级嵌入式系统中调用

#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include


#ifndef ICMP_ECHOREPLY
#define ICMP_ECHOREPLY        0
#endif


#ifndef ICMP_ECHO
#define ICMP_ECHO 8
#endif


#ifndef bool
#define bool int
#endif


#ifndef true
#define true 1
#endif


#ifndef false
#define false 0
#endif




#define XM_ICMP_LEN  (56 + 8)      //ICMP默认数据长度 + ICMP默认头部长度
#define SEND_BUFFER_SIZE 128 //发送缓冲区大小
#define RECV_BUFFER_SIZE 128 //接收缓冲区大小


//IP报头  小端结构 第一个字节,低4位是 version 高四位是 length 
//IP报头  大端结构 第一个字节,低4位是 length 高四位是 version 
//接收报文失败,可尝试交换 ip_hl ip_v位置,或者取icmp报文时候直接跳过20个byte
typedef struct xm_ip  
{  
unsigned int ip_hl:4; //header length(报头长度)   
unsigned int ip_v:4; //version(版本)   
unsigned char ip_tos;  
unsigned short ip_len;  
unsigned short ip_id;  
unsigned short ip_off;  
unsigned char ip_ttl;  
unsigned char ip_p;  
unsigned short ip_sum;  
struct in_addr ip_src;  
struct in_addr ip_dst;  
}IP_HEADER;


struct xm_icmp
{
    unsigned char icmp_type;
    unsigned char icmp_code;
    unsigned short icmp_cksum;
    union
    {
        struct
        {
            unsigned short icmp_id;
            unsigned short icmp_sequence;
        }echo;
        
        unsigned int gateway;
        struct
        {
            unsigned short icmp_unused;
            unsigned short icmp_mtu;
        }frag; //pmtu发现
    }un;
    unsigned char icmp_data[0];
#define icmp_id un.echo.icmp_id
#define icmp_seq un.echo.icmp_sequence
};




static bool XM_SendPacket(int sock_icmp, struct sockaddr_in *dest_addr, int nSend)
{


char SendBuffer[SEND_BUFFER_SIZE] = {0};
struct xm_icmp *pIcmp = (struct xm_icmp*)SendBuffer;
struct timeval *pTime; 
unsigned short *data = (unsigned short *)pIcmp;
int len = XM_ICMP_LEN;
unsigned int sum = 0;

/* 类型和代码分别为ICMP_ECHO,0代表请求回送 */
pIcmp->icmp_type = ICMP_ECHO;
pIcmp->icmp_code = 0;
pIcmp->icmp_cksum = 0; //校验和
pIcmp->icmp_seq = nSend; //序号
pIcmp->icmp_id = sock_icmp; //取描述符作为标志
pTime = (struct timeval *)pIcmp->icmp_data;
gettimeofday(pTime, NULL); //数据段存放发送时间


while (len > 1)
{
sum += *data++;
len -= 2;
}

if (1 == len)
{
unsigned short tmp = *data;
tmp &= 0xff00;
sum += tmp;
}
 
//ICMP校验和带进位
while (sum >> 16)
sum = (sum >> 16) + (sum & 0x0000ffff);
sum = ~sum;

pIcmp->icmp_cksum =sum;

if (sendto(sock_icmp, SendBuffer, XM_ICMP_LEN, 0,(struct sockaddr *)dest_addr, sizeof(struct sockaddr_in)) < 0)
{
printf("%s:%d  sendto failure err[%d]%s\n",__FUNCTION__,__LINE__,errno,strerror(errno));
return false;
}
return true;
}
 
static bool XM_RecvePacket(int sock_icmp, struct sockaddr_in *dest_addr)
{
int RecvBytes = 0;
char RecvBuffer[RECV_BUFFER_SIZE] = {0};
int addrlen = sizeof(struct sockaddr_in); 
struct xm_ip *Ip = (struct xm_ip *)RecvBuffer;
struct xm_icmp *Icmp;
struct timeval RecvTime;
int ipHeadLen = 0;
double rtt;

if ((RecvBytes = recvfrom(sock_icmp, RecvBuffer, RECV_BUFFER_SIZE,0, (struct sockaddr *)dest_addr, &addrlen)) < 0)
{
printf("%s:%d  recvfrom failure err[%d]%s\n",__FUNCTION__,__LINE__,errno,strerror(errno));
return false;
}
ipHeadLen = (Ip->ip_hl << 2);
 
gettimeofday(&RecvTime, NULL);
Icmp = (struct xm_icmp *)(RecvBuffer + ipHeadLen);
//判断接收到的报文是否是自己所发报文的响应
if ((Icmp->icmp_type == ICMP_ECHOREPLY) && Icmp->icmp_id == sock_icmp)
{
struct timeval *SendTime = (struct timeval *)Icmp->icmp_data;


if ((RecvTime.tv_usec -= SendTime->tv_usec) < 0)
{
--(RecvTime.tv_sec);
RecvTime.tv_usec += 1000000;
}
RecvTime.tv_sec -= SendTime->tv_sec;


rtt = RecvTime.tv_sec * 1000.0 + RecvTime.tv_usec / 1000.0;
printf("%u bytes from %s:icmp_seq=%u ttl=%u time=%f ms\n",(ntohs(Ip->ip_len) - ipHeadLen),inet_ntoa(Ip->ip_src),Icmp->icmp_seq,Ip->ip_ttl,rtt);
return true;
}



return true; 
}




 
bool XM_Ping(char *pszHost, int nTimes,int nTransTimeout, int *pnSussessCount)
{
int nSend = 0;
int sock_icmp = 0; //icmp套接字
struct sockaddr_in dest_addr; //IPv4专用socket地址,保存目的地址
 
 
    struct timeval timeout = {nTransTimeout / 1000, nTransTimeout % 1000 * 1000};
    struct addrinfo stHints, *p_res,*p_cur;
*pnSussessCount = 0;


    memset(&stHints, 0, sizeof(struct addrinfo));
    stHints.ai_family = PF_INET;
    stHints.ai_flags = AI_PASSIVE;
    stHints.ai_protocol = IPPROTO_ICMP;
    stHints.ai_socktype = SOCK_RAW;        

    if (-1 == getaddrinfo(pszHost, NULL, &stHints, &p_res))
{
printf("%s:%d  getaddrinfo failure err[%d]%s\n",__FUNCTION__,__LINE__,errno,strerror(errno));
return false;
    }

if(!p_res)
{
printf("%s:%d  getaddrinfo failure err[%d]%s\n",__FUNCTION__,__LINE__,errno,strerror(errno));
return false;
}

    for (p_cur = p_res; p_cur != NULL; p_cur = p_cur->ai_next) 
{
int nCount = 0;
if((p_cur->ai_family != PF_INET)||(p_cur->ai_socktype != SOCK_RAW)||(p_cur->ai_protocol != IPPROTO_ICMP))
{
printf("%s:%d  [%d %d %d %d %d %d]\n",__FUNCTION__,__LINE__,p_cur->ai_family,PF_INET,p_cur->ai_socktype,SOCK_RAW,p_cur->ai_protocol, IPPROTO_ICMP);
continue;
}
for(nCount = 0; nCount < 10; nCount++)
{
sock_icmp = socket(p_cur->ai_family, p_cur->ai_socktype, p_cur->ai_protocol);
if(sock_icmp > 0)
{
memcpy(&dest_addr,p_cur->ai_addr,sizeof(struct sockaddr_in));
break;
}
sleep(1);
}
if(nCount < 10)
{
break;
}
printf("%s:%d  socket failure err[%d]%s\n",__FUNCTION__,__LINE__,errno,strerror(errno));
    }
    freeaddrinfo(p_res);

if(sock_icmp <= 0)
{
printf("%s:%d  socket failure err[%d]%s\n",__FUNCTION__,__LINE__,errno,strerror(errno));
return false;
}


    if(-1 == setsockopt(sock_icmp, SOL_SOCKET, SO_SNDTIMEO, (char *)&timeout,sizeof(struct timeval)))
    {
printf("%s:%d  setsockopt failure err[%d]%s\n",__FUNCTION__,__LINE__,errno,strerror(errno));
return false;
    }


    if(-1 == setsockopt(sock_icmp, SOL_SOCKET, SO_RCVTIMEO, (char *)&timeout,sizeof(struct timeval)))
    {
printf("%s:%d  setsockopt failure err[%d]%s\n",__FUNCTION__,__LINE__,errno,strerror(errno));
return false;
    }


printf("(%s) %d bytes of data\n",inet_ntoa(dest_addr.sin_addr), XM_ICMP_LEN);


 
while (nSend < nTimes)
{
if(!XM_SendPacket(sock_icmp, &dest_addr, nSend))
{
nSend++;
continue;
}
nSend++;

if(!XM_RecvePacket(sock_icmp, &dest_addr))
{
continue;
}
(*pnSussessCount)++;
}

return true;
}
 
bool main(int argc, char *argv[])
{
int nSussessCount = 0;


if(argc < 4)
{
printf("usage: ./a.out url[www.baidu.com] count[10] timeout[100]ms\n");
return false;
}

XM_Ping(argv[1],atoi(argv[2]),atoi(argv[3]),&nSussessCount);

printf("ping %s nTotalCount:%d nSussessCount :%d\n",argv[1],atoi(argv[2]),nSussessCount);


return true;


}





你可能感兴趣的:(ping,icmp,ip)