想实现一个在没外网的时候就自动重启路由器的功能。
又不想用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//效验算法(百度下有注释,但是还是看不太明白) unsignedshort cal_chksum(unsignedshort *addr,int len) {int nleft=len;int sum=0; unsignedshort *w=addr; unsignedshort answer=0;while(nleft >1) { sum+= *w++; nleft-=2; }if( nleft ==1) {*(unsignedchar *)(&answer) = *(unsignedchar *)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_infrom;// 设定Ip信息 bzero(&addr,sizeof(addr)); addr.sin_family= AF_INET; addr.sin_addr.s_addr= inet_addr(ips);#if 1int 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((unsignedshort *)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; } }#endifreturn SUCCESS; }int main() {#if 1char cPing[16]; printf("Please input ping IP:"); scanf("%s",cPing);#elsechar *cPing ="192.168.1.200";#endifif(ping(cPing,10000)) { printf("Ping succeed!\n"); }else { printf("Ping wrong!\n"); }return0; }
0)直接用参考链接上的代码时编译不过,不知道是不是因为我用的是cpp,没太深究。
1)实际使用的时候需要加上sudo,不然在创建套接字那个地方会报错。我还没想好怎么在代码里用sudo,(因为实际项目运行起来是不需要加sudo的)。