码迷,mamicode.com
首页 > 其他好文 > 详细

用socket实现ping功能(记录)

时间:2016-03-12 11:44:48      阅读:598      评论:0      收藏:0      [点我收藏+]

标签:

/*

 参考代码 http://bbs.csdn.net/topics/230001156

 原文为win32版本

 这里已修改为unix版本

 */

 

#include <stdio.h>

#include <string.h>

#include <errno.h>

#include <unistd.h>

#include <sys/time.h>

#include <sys/socket.h>

#include <sys/proc.h>

#include <sys/ioctl.h>

#include <netinet/in.h>

#include <netinet/tcp.h>

#include <arpa/inet.h>

#include <stdio.h>

#include <netdb.h> // gethostbyname

 

typedef int SOCKET;

typedef struct icmp_hdr

{

unsigned char   icmp_type;        // 消息类型

unsigned char   icmp_code;        // 代码

unsigned short  icmp_checksum;    // 校验和

// 下面是回显头

unsigned short  icmp_id;        // 用来惟一标识此请求的ID号

unsigned short  icmp_sequence;  // 序列号

unsigned long   icmp_timestamp; // 时间戳

} ICMP_HDR, *PICMP_HDR;

 

typedef struct _IPHeader        // 20字节的IP头

{

uint8_t     iphVerLen;      // 版本号和头长度(各占4位)

uint8_t     ipTOS;          // 服务类型

uint16_t    ipLength;       // 封包总长度,即整个IP报的长度

uint16_t    ipID;              // 封包标识,惟一标识发送的每一个数据报

uint16_t    ipFlags;          // 标志

uint8_t     ipTTL;          // 生存时间,就是TTL

uint8_t     ipProtocol;     // 协议,可能是TCP、UDP、ICMP等

uint16_t    ipChecksum;     // 校验和

uint32_t    ipSource;       // 源IP地址

uint32_t    ipDestination;  // 目标IP地址

} IPHeader, *PIPHeader;

 

uint16_t checksum(uint16_t* buff, int size);

 

bool SetTimeout(SOCKET s, int nTime, bool bRecv = TRUE);

 

int main(int args, const char ** argv)

{

// 目的IP地址,即要Ping的IP地址

const char * szDestIp = (args > 1)?argv[1]:"127.0.0.1";

uint16_t pID = (uint16_t)::time(0);

 

// 创建原始套节字

SOCKET sRaw = ::socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);

 

if(sRaw == -1)

{

auto eno = errno;

if(eno == 1)

{

// 必须使用sudo才能建立Raw socket

printf("Operation not permitted!\n");

}

else

{

printf("Cannot create socket! Error %d", eno);

}

return -1;

}

// 设置接收超时

if(!SetTimeout(sRaw, 1000, TRUE))

{

printf("Cannot set timeout!\n");

}

 

// 设置目的地址

sockaddr_in dest;

dest.sin_family = AF_INET;

dest.sin_port = htons(0);

dest.sin_addr.s_addr = inet_addr(szDestIp);

 

// 创建ICMP封包

char buff[sizeof(ICMP_HDR) + 32];

ICMP_HDR* pIcmp = (ICMP_HDR*)buff;

 

// 填写ICMP封包数据

pIcmp->icmp_type = 8;    // 请求一个ICMP回显

pIcmp->icmp_code = 0;

pIcmp->icmp_id = (uint16_t)pID;

pIcmp->icmp_checksum = 0;

pIcmp->icmp_sequence = 0;

// 填充数据部分,可以为任意

memset(&buff[sizeof(ICMP_HDR)], ‘E‘, 32);

 

// 开始发送和接收ICMP封包

uint16_t    nSeq = 0;

char recvBuf[1024];

sockaddr_in from;

socklen_t nLen = sizeof(from);

while(TRUE)

{

static int nCount = 0;

long nRet;

if(nCount++ == 4)

break;

pIcmp->icmp_checksum = 0;

pIcmp->icmp_timestamp = ::clock();

pIcmp->icmp_sequence = nSeq++;

pIcmp->icmp_checksum = checksum((uint16_t*)buff, sizeof(ICMP_HDR) + 32);

nRet = (long)::sendto(sRaw, buff, sizeof(ICMP_HDR) + 32,

0, (sockaddr *)&dest, sizeof(dest));

if(nRet == -1)

{

printf(" sendto() failed: %d \n", errno);

return -1;

}

nRet = (long)::recvfrom(sRaw, recvBuf, 1024, 0, (sockaddr*)&from, &nLen);

if(nRet == -1)

{

printf(" recvfrom() failed: %d\n", errno);

return -1;

}

 

// 下面开始解析接收到的ICMP封包

auto nTick = ::clock();

if(nRet < sizeof(IPHeader) + sizeof(ICMP_HDR))

{

printf(" Too few bytes from %s \n", ::inet_ntoa(from.sin_addr));

}

// 接收到的数据中包含IP头,IP头大小为20个字节,所以加20得到ICMP头

ICMP_HDR* pRecvIcmp = (ICMP_HDR*)(recvBuf + 20); // (ICMP_HDR*)(recvBuf + sizeof(IPHeader));

if(pRecvIcmp->icmp_type != 0)    // 回显

{

printf(" nonecho type %d recvd \n", pRecvIcmp->icmp_type);

return -1;

}

 

if(pRecvIcmp->icmp_id != pID)

{

printf(" someone else‘s packet! \n");

return -1;

}

 

printf(" %d bytes from %s:", (int)nRet, inet_ntoa(from.sin_addr));

printf(" icmp_seq = %d. ", pRecvIcmp->icmp_sequence);

printf(" time: %d ms", (int)nTick - (int)pRecvIcmp->icmp_timestamp);

printf(" \n");

 

}

 

return 0;

}

 

uint16_t checksum(uint16_t* buff, int size)

{

unsigned long cksum = 0;

while(size>1)

{

cksum += *buff++;

size -= sizeof(uint16_t);

}

// 是奇数

if(size)

{

cksum += *(uint8_t*)buff;

}

// 将32位的chsum高16位和低16位相加,然后取反

while(cksum >> 16)

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

return (uint16_t)(~cksum);

}

 

bool SetTimeout(SOCKET s, int nTime, bool bRecv)

{

int ret = ::setsockopt(s, SOL_SOCKET,

  bRecv ? SO_RCVTIMEO : SO_SNDTIMEO, (char*)&nTime, sizeof(nTime));

return ret != -1;

}

用socket实现ping功能(记录)

标签:

原文地址:http://www.cnblogs.com/leaving/p/5268203.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!