/*

参考 http://bbs.csdn.net/topics/230001156 原文为win32版本

稍有改动,以适应mac与linux系统

*/

#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);

inline uint32_t GetIpByName(const char * name)

{

hostent* pHostent = gethostbyname(name);

if(pHostent == nullptr) return INADDR_NONE;

uint32_t host = *(uint32_t*)pHostent->h_addr_list[0];

return host;

}

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

{

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

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

auto ip = inet_addr(szDestIp);

if((int)ip == -1) ip = GetIpByName(szDestIp); // nds

if((int)ip == -1)

{

printf("invalid ip address:%s\n", szDestIp);

return -1;

}

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

printf("ping %s\n", szDestIp);

// 创建原始套节字

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 = ip;

// 创建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 0 // IP头解析

IPHeader * header = (IPHeader*)recvBuf;

struct in_addr a;

a.s_addr = header->ipSource;

printf("source ip %s\n", inet_ntoa(a));

a.s_addr = header->ipDestination;

printf("dest ip %s\n", inet_ntoa(a));

#endif

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功能(记录)的更多相关文章

  1. Linux下实现ping功能

    实现ping功能,就肯定要用到ping命令,那么在Linux下ping命令为: ping [-dfnqrRv][-c<完成次数>][-i<间隔秒数>][-I<网络界面&g ...

  2. 转 My97日历控件常用功能记录

    My97相信大家都不陌生,应该是我所见过的最强大的一个日历控件了,最近的项目中也比较多地用到了此控件,而且项目中经常会有不同时间范围的需求,在此列出一些比较常用的日期范围格式的设置,尽管在My97的官 ...

  3. socket实现聊天功能(二)

    socket实现聊天功能(二) WebSocket协议是建立在HTTP协议之上,因此创建websocket服务时需要调用http模块的createServer方法.将生成的server作为参数传入so ...

  4. 前端常用功能记录(二)—datatables表格(转)

    前端常用功能记录(二)—datatables表格 并不是所有的后台开发都有美工和前端工程师来配合做页面,为了显示数据并有一定的美感,jQuery的DataTables插件对于像我这样的前端菜鸟来说真是 ...

  5. js小功能记录

    个人日常中遇到的js小功能记录,方便查看. /** * 判断是否包含字符串某字符串 * @param {[type]} str [被检测的字符串] * @param {[type]} substr [ ...

  6. Java实现ping功能的三种方法及Linux的区分

    前大半部份转自:https://blog.csdn.net/futudeniaodan/article/details/52317650 检测设备的运行状态,有的是使用ping的方式来检测的.所以需要 ...

  7. 你知道PING功能是怎么实现的吗

    以太网的协议有层,而每层都包含有更多的协议.所谓协议,通俗的讲就是通信双方约定的规则. 今天我们介绍一些一个听起来陌生却有很常用的协议,ICMP协议. —ICMP是(Internet Control ...

  8. 如何开放 Azure 虚拟机 Ping 功能

    前言 文章<使用 PsPing & PaPing 进行 TCP 端口连通性测试>中提到,ICMP 协议的数据包无法通过 Azure 的防火墙和负载均衡器,所以不能直接使用 Ping ...

  9. 【虚拟机-网络IP】如何开放 Azure 虚拟机 Ping 功能

    前言 文章<使用 PsPing & PaPing 进行 TCP 端口连通性测试>中提到,ICMP 协议的数据包无法通过 Azure 的防火墙和负载均衡器,所以不能直接使用 Ping ...

随机推荐

  1. 在eclipse上开发nodejs

    首先到官网下载nodejs.地址:https://nodejs.org/en,可根据自己的操作系统选择下载. 安装好后.进入命令行输入node ,然后输入console.log("hello ...

  2. Firebug 学习使用教程

    Firebug是Firefox下的一个扩展,能够调试所有网站语言,如Html.Css等,但FireBug最吸引人的就是JavaScript调试功能,除此之外,还有对Dom的查看与调试,网站整体分析等其 ...

  3. 巧用margin/padding的百分比值实现高度自适应(多用于占位,避免闪烁)

    本文依赖于一个基础却又容易混淆的css知识点:当margin/padding取形式为百分比的值时,无论是left/right,还是top/bottom,都是以父元素的width为参照物的!也许你会说, ...

  4. 日文xp系统中 日文键盘模式转英式键盘模式

    键盘设备驱动早在Windows XP安装时就已经安装好了,但是系统却将日式键盘误识成了美式标准键盘,这会出现一些标点符号的实际输入与键盘标注不符的问题,对于用惯了英文键盘的人可 以盲打而不去理会,但对 ...

  5. Class.forName()用法及与new区别

    平时开发中我们经常会发现:用到Class.forName()方法.为什么要用呢? 下面分析一下: 主要功能Class.forName(xxx.xx.xx)返回的是一个类Class.forName(xx ...

  6. JAVA 中XML的解析

    XML:  可扩展标记语言(extensible Markup Language) 用于标记电子文件使其具有结构性的标记语言.XML可以用来标记数据.定义数据类型,是一种允许用户对自己的标记语言进行定 ...

  7. up_modembin.sh

    --- 编译modem生成bin文件cp到指定文件夹 up_modembin.sh #!/bin/bash flag=${} == $flag ];then # echoMsg find vendor ...

  8. 面对对象之@classmethod、@staticmethod用法

    @classmethod用法(修饰的函数,第一个参数cls默认是类名,调用方法:实例对象或类对象.方法) class C_mthod(object): name = "f**k" ...

  9. CentOS 7.2.1511编译安装Nginx1.10.1+MySQL5.6.33+PHP5.6.26

    准备篇 一.防火墙配置 CentOS 7.x默认使用的是firewall作为防火墙,这里改为iptables防火墙. 1.关闭firewall: systemctl stop firewalld.se ...

  10. 关于IONIC 报错 XX is not a function

    刚开始 做一个项目,总是报错"XX is not  a function"   最后发现 原因 ,   原来是 服务的 注入位置 有问题. angular.module(" ...