Linux C++ 实现一个简易版的ping (也就是imcp协议)
背景:
想实现一个在没外网的时候就自动重启路由器的功能。
又不想用ping命令,因为在代码里调用system("ping"); 可能会比较耗时,得单开线程。于是找了个实现ICMP协议的代码。
参考:https://blog.csdn.net/qivan/article/details/7237051
代码:
#include <stdio.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <netinet/ip_icmp.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <sys/time.h>
#include <unistd.h>
#include <netdb.h>
#include <string.h> #define PACKET_SIZE 4096
#define ERROR 0
#define SUCCESS 1 //效验算法(百度下有注释,但是还是看不太明白)
unsigned short cal_chksum(unsigned short *addr, int len)
{
int nleft=len;
int sum=0;
unsigned short *w=addr;
unsigned short answer=0; while(nleft > 1)
{
sum += *w++;
nleft -= 2;
} if( nleft == 1)
{
*(unsigned char *)(&answer) = *(unsigned char *)w;
sum += answer;
} sum = (sum >> 16) + (sum & 0xffff);
sum += (sum >> 16);
answer = ~sum; return answer;
}
// Ping函数
int ping( char *ips, int timeout)
{
struct timeval *tval;
int maxfds = 0;
fd_set readfds; struct sockaddr_in addr;
struct sockaddr_in from;
// 设定Ip信息
bzero(&addr,sizeof(addr));
addr.sin_family = AF_INET; addr.sin_addr.s_addr = inet_addr(ips); #if 1
int sockfd;
// 取得socket 。 如果没加sudo 这里会报错
sockfd = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);
if (sockfd < 0)
{
printf("ip:%s,socket error\n",ips);
return ERROR;
} struct timeval timeo;
// 设定TimeOut时间
timeo.tv_sec = timeout / 1000;
timeo.tv_usec = timeout % 1000; if (setsockopt(sockfd, SOL_SOCKET, SO_SNDTIMEO, &timeo, sizeof(timeo)) == -1)
{
printf("ip:%s,setsockopt error\n",ips);
return ERROR;
} char sendpacket[PACKET_SIZE];
char recvpacket[PACKET_SIZE];
// 设定Ping包
memset(sendpacket, 0, sizeof(sendpacket)); pid_t pid;
// 取得PID,作为Ping的Sequence ID
pid=getpid(); struct ip *iph;
struct icmp *icmp; icmp=(struct icmp*)sendpacket;
icmp->icmp_type=ICMP_ECHO; //回显请求
icmp->icmp_code=0;
icmp->icmp_cksum=0;
icmp->icmp_seq=0;
icmp->icmp_id=pid;
tval= (struct timeval *)icmp->icmp_data;
gettimeofday(tval,NULL);
icmp->icmp_cksum=cal_chksum((unsigned short *)icmp,sizeof(struct icmp)); //校验 int n;
// 发包 。可以把这个发包挪到循环里面去。
n = sendto(sockfd, (char *)&sendpacket, sizeof(struct icmp), 0, (struct sockaddr *)&addr, sizeof(addr));
if (n < 1)
{
printf("ip:%s,sendto error\n",ips);
return ERROR;
} // 接受
// 由于可能接受到其他Ping的应答消息,所以这里要用循环
while(1)
{
// 设定TimeOut时间,这次才是真正起作用的
FD_ZERO(&readfds);
FD_SET(sockfd, &readfds);
maxfds = sockfd + 1;
n = select(maxfds, &readfds, NULL, NULL, &timeo);
if (n <= 0)
{
printf("ip:%s,Time out error\n",ips);
close(sockfd);
return ERROR;
} // 接受
memset(recvpacket, 0, sizeof(recvpacket));
int fromlen = sizeof(from);
n = recvfrom(sockfd, recvpacket, sizeof(recvpacket), 0, (struct sockaddr *)&from, (socklen_t *)&fromlen);
printf("recvfrom Len:%d\n",n);
if (n < 1)
{
return ERROR;
} char *from_ip = (char *)inet_ntoa(from.sin_addr);
// 判断是否是自己Ping的回复
if (strcmp(from_ip,ips) != 0)
{
printf("NowPingip:%s Fromip:%s NowPingip is not same to Fromip,so ping wrong!\n",ips,from_ip);
return ERROR;
} iph = (struct ip *)recvpacket; icmp=(struct icmp *)(recvpacket + (iph->ip_hl<<2)); printf("ip:%s,icmp->icmp_type:%d,icmp->icmp_id:%d\n",ips,icmp->icmp_type,icmp->icmp_id);
// 判断Ping回复包的状态
if (icmp->icmp_type == ICMP_ECHOREPLY && icmp->icmp_id == pid) //ICMP_ECHOREPLY回显应答
{
// 正常就退出循环
printf("icmp succecss ............. \n");
break;
}
else
{
// 否则继续等
continue;
}
}
#endif
return SUCCESS;
} int main()
{
#if 1
char cPing[16];
printf("Please input ping IP:");
scanf("%s",cPing);
#else
char *cPing = "192.168.1.200";
#endif
if(ping(cPing,10000))
{
printf("Ping succeed!\n");
}
else
{
printf("Ping wrong!\n");
} return 0;
}
实际效果:

补充说明:
0)直接用参考链接上的代码时编译不过,不知道是不是因为我用的是cpp,没太深究。
1)实际使用的时候需要加上sudo,不然在创建套接字那个地方会报错。我还没想好怎么在代码里用sudo,(因为实际项目运行起来是不需要加sudo的)。
Linux C++ 实现一个简易版的ping (也就是imcp协议)的更多相关文章
- .NET Core的文件系统[5]:扩展文件系统构建一个简易版“云盘”
FileProvider构建了一个抽象文件系统,作为它的两个具体实现,PhysicalFileProvider和EmbeddedFileProvider则分别为我们构建了一个物理文件系统和程序集内嵌文 ...
- 依赖注入[5]: 创建一个简易版的DI框架[下篇]
为了让读者朋友们能够对.NET Core DI框架的实现原理具有一个深刻而认识,我们采用与之类似的设计构架了一个名为Cat的DI框架.在<依赖注入[4]: 创建一个简易版的DI框架[上篇]> ...
- 依赖注入[4]: 创建一个简易版的DI框架[上篇]
本系列文章旨在剖析.NET Core的依赖注入框架的实现原理,到目前为止我们通过三篇文章(<控制反转>.<基于IoC的设计模式>和< 依赖注入模式>)从纯理论的角度 ...
- .NET CORE学习笔记系列(2)——依赖注入[4]: 创建一个简易版的DI框架[上篇]
原文https://www.cnblogs.com/artech/p/net-core-di-04.html 本系列文章旨在剖析.NET Core的依赖注入框架的实现原理,到目前为止我们通过三篇文章从 ...
- 手动实现一个简易版SpringMvc
版权声明:本篇博客大部分代码引用于公众号:java团长,我只是在作者基础上稍微修改一些内容,内容仅供学习与参考 前言:目前mvc框架经过大浪淘沙,由最初的struts1到struts2,到目前的主流框 ...
- 如何实现一个简易版的 Spring - 如何实现 Setter 注入
前言 之前在 上篇 提到过会实现一个简易版的 IoC 和 AOP,今天它终于来了...相信对于使用 Java 开发语言的朋友们都使用过或者听说过 Spring 这个开发框架,绝大部分的企业级开发中都离 ...
- 如何实现一个简易版的 Spring - 如何实现 Constructor 注入
前言 本文是「如何实现一个简易版的 Spring」系列的第二篇,在 第一篇 介绍了如何实现一个基于 XML 的简单 Setter 注入,这篇来看看要如何去实现一个简单的 Constructor 注入功 ...
- 如何实现一个简易版的 Spring - 如何实现 @Component 注解
前言 前面两篇文章(如何实现一个简易版的 Spring - 如何实现 Setter 注入.如何实现一个简易版的 Spring - 如何实现 Constructor 注入)介绍的都是基于 XML 配置文 ...
- 使用 js 和 Beacon API 实现一个简易版的前端埋点监控 npm 包
使用 js 和 Beacon API 实现一个简易版的前端埋点监控 npm 包 前端监控,埋点,数据收集,性能监控 Beacon API https://caniuse.com/beacon 优点,请 ...
随机推荐
- linux大工程 - 我要一个属于自己的回收站
我要开始装13了 'rm -rf 是一个很"粗鲁"的命令,就像windows的shift+delete,删除的文件是无法找回的(当然,除了数据恢复软件,但是很麻烦,很费时)' '为 ...
- verification 提取差异点
提取出差异点 传统用例 项目A锁定的寄存器是regA,项目B的锁定功能的寄存器是regB,如果项目A用例中直接用 reg_model.regA.write(); reg_model.regA.read ...
- 面向对象—继承(Day19)
初识继承 继承是一种创建新类的方式,在python中新类可以继承一个或多个父类,父类又可称为基类或超类,新建的类为派生类或子类.(类与类之间的关系是什么是什么的关系.) 继承语法: class Par ...
- Dubbo扩展点应用之六服务动态降级
服务降级,当服务器压力剧增的情况下,根据当前业务情况及流量对一些服务有策略的降低服务级别以释放服务器资源保证核心任务的政策运行. 为什么要使用服务降级呢?这是为了防止分布式服务发送雪崩效应,也就是蝴蝶 ...
- JUC并发工具类之 CyclicBarrier同步屏障
首先看看CyclicBarrier的使用场景: 10个工程师一起来公司应聘,招聘方式分为笔试和面试.首先,要等人到齐后,开始笔试:笔试结束之后,再一起参加面试.把10个人看作10个线程,10个线程之间 ...
- 树莓派PICO刷入MicroPython内核的两种方式
一.MicroPython简介 MicroPython 是 Python 3编程语言的精简高效实现,其中包括 Python 标准库的一小部分,并且经过优化,可在微控制器和受限环境中运行. 可以在 25 ...
- docker安装sentinel-dashbord
查找 sentinel-dashboard docker search sentinel-dashboard pull 镜像 docker pull bladex/sentinel-dashboard ...
- 设置maven创建工程的jdk编译版本
方式一:在maven的主配置文件中指定创建工程时使用jdk1.8版本 <profile> <id>jdk-1.8</id> <activation> & ...
- Python_time库_特定字符串格式的时间、struct_time、时间戳的处理
time库 时间戳:格林威治时间1970年01月01日00时00分00秒(北京时间1970年01月01日08时00分00秒)起至现在的总秒数. # time.strptime(),功能:将特定字符串格 ...
- S32Kxxx bootloader之CAN bootloader
了解更多关于bootloader 的C语言实现,请加我Q扣: 1273623966 (验证信息请填 bootloader),欢迎咨询或定制bootloader(在线升级程序). 最近完成了S32Kxx ...