用socket实现ping功能(记录)
/*
参考 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功能(记录)的更多相关文章
- Linux下实现ping功能
实现ping功能,就肯定要用到ping命令,那么在Linux下ping命令为: ping [-dfnqrRv][-c<完成次数>][-i<间隔秒数>][-I<网络界面&g ...
- 转 My97日历控件常用功能记录
My97相信大家都不陌生,应该是我所见过的最强大的一个日历控件了,最近的项目中也比较多地用到了此控件,而且项目中经常会有不同时间范围的需求,在此列出一些比较常用的日期范围格式的设置,尽管在My97的官 ...
- socket实现聊天功能(二)
socket实现聊天功能(二) WebSocket协议是建立在HTTP协议之上,因此创建websocket服务时需要调用http模块的createServer方法.将生成的server作为参数传入so ...
- 前端常用功能记录(二)—datatables表格(转)
前端常用功能记录(二)—datatables表格 并不是所有的后台开发都有美工和前端工程师来配合做页面,为了显示数据并有一定的美感,jQuery的DataTables插件对于像我这样的前端菜鸟来说真是 ...
- js小功能记录
个人日常中遇到的js小功能记录,方便查看. /** * 判断是否包含字符串某字符串 * @param {[type]} str [被检测的字符串] * @param {[type]} substr [ ...
- Java实现ping功能的三种方法及Linux的区分
前大半部份转自:https://blog.csdn.net/futudeniaodan/article/details/52317650 检测设备的运行状态,有的是使用ping的方式来检测的.所以需要 ...
- 你知道PING功能是怎么实现的吗
以太网的协议有层,而每层都包含有更多的协议.所谓协议,通俗的讲就是通信双方约定的规则. 今天我们介绍一些一个听起来陌生却有很常用的协议,ICMP协议. ICMP是(Internet Control ...
- 如何开放 Azure 虚拟机 Ping 功能
前言 文章<使用 PsPing & PaPing 进行 TCP 端口连通性测试>中提到,ICMP 协议的数据包无法通过 Azure 的防火墙和负载均衡器,所以不能直接使用 Ping ...
- 【虚拟机-网络IP】如何开放 Azure 虚拟机 Ping 功能
前言 文章<使用 PsPing & PaPing 进行 TCP 端口连通性测试>中提到,ICMP 协议的数据包无法通过 Azure 的防火墙和负载均衡器,所以不能直接使用 Ping ...
随机推荐
- 数据库备份checksum选项你会用么?
SQL SERVER有好多好多功能,选项也一大堆,很多功能选项并不常用.但是如果真有这种需求的时候又想不起来~ 本篇我们就来聊聊备份里的选项checksum,这是个啥玩意?听都没听过?来看下图: 就是 ...
- WebBrowser内核指定
一.背景 这几天在维护公司的一个项目,嗯…到现在七八年没人动过了(也是老古董了),都说N年前的代码碰不得 处处是坑 不能挖坑还得一步一步的填坑,恰好今天就填了一坑 此处作为记录 供以后翻阅,对代码除了 ...
- robots.txt文件没错,为何总提示封禁
大家好,我的robots.txt文件没错,为何百度总提示封禁,哪位高人帮我看看原因,在此谢过. 我的站点www.haokda.com,robots.txt如下: ## robots.txt for P ...
- wex5 实战 登陆帐号更换与用户id一致性
一 前言 关于用户登陆,注册,页面跳转,我讲了很多,都是页面框架级别的.有人问到这个问题,索性总结一下,今天是收尾最后一篇. 以往初学时的设计是: 1 注册 2 登陆 3 进入页面. 这种很简单,没有 ...
- php变量 写时改变 写时复制
写时复制 $var = 1; $var2 = $var; #此时$var2 与 $var 指向同一个zval refcount = 2: $var = 2; # 此时$val 改变 所以 $var 与 ...
- 8. apache服务实验笔记
Apache服务器 一 简介 1 www:world wide web 万维网 http 协议: 超文本传输协议 HTML语言: 超文本标识语言 2 URL:统一资源定位 协议+域名:端口+网页文 ...
- mysql登陆问题
1.centos6下mysql5.7的登陆问题 问题的描述:ERROR 1045 (28000): Access denied for user 'root'@'localhost' (using p ...
- 常用正则表达式大全,手机、电话、邮箱、身份证(最严格的验证)、IP地址、网址、日期等
<script type="text/javascript">/* * 手机号码格式 * 只允许以13.15.18开头的号码 * 如:13012345678.15929 ...
- html5本地存储(localStorage)使用介绍
1.html5几种存储形式 本地存储(localStorage && sessionStorage) 离线缓存(application cache) indexedDB 和 webSQ ...
- 微信touchmove不生效
最近在写一个微信里面滑动切换图片的功能,发现在chrome下都正常显示,可是在微信和qq浏览器里面就是不行. 经过一番排查,发现了问题: touchmove只触发了一次. 解决方案: 在touchst ...