1. 程式人生 > 其它 >【部落格407】用socket程式設計實現ping命令

【部落格407】用socket程式設計實現ping命令

技術標籤:linux

內容:使用socket程式設計實現ping命令

socket程式設計原始碼:

/*
** time:2020.12.27
** 功能:實現ping命令
** 環境:ubuntu
*/

#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<netinet/ip.h>
#include<netinet/ip_icmp.h>
#include<signal.h> #include<arpa/inet.h> #include<sys/time.h> #include<string.h> #include<netdb.h> #include<pthread.h> #define PACK_SIZE 4096 //使用的結構宣告 char send_packet[PACK_SIZE]; //傳送報文結構 char recv_packet[PACK_SIZE]; //接收報文結構 int sockfd; //套接字 pid_t pid;
//程序號 int begin=0,end=0; //記錄發收的包的數量 int datalen=56; //icmp資料長度,注意完成資料報文最小為64bytes,不是56bytes struct timeval recv_time; //記錄某一刻的時間 struct sockaddr_in from; //使用的函式宣告 void* send_data(void* arg); //發資料函式 void* recv_data(void* arg); //收資料函式
unsigned short get_cksum(unsigned char*,int); //校驗和計算 void time_sub(struct timeval*,struct timeval*); //計算時間差 //各個函式實現 //報文傳送函式 void* send_data(void* arg) { int packetsize; int i=0; for(;i<4;i++) { packetsize=pack(i) if(sendto(sockfd,send_packet,packetsize,0,(struct sockaddr*)&from,sizeof(from))<0) //傳送報文 { //失敗 perror("sendto"); continue; } else { begin++; //成功後,休眠1s sleep(1); } } return NULL; } //報文接收資料 void* recv_data(void* arg) { int n,fromlen; fromlen=sizeof(from); int i=0; while(i<4) { if((n=recvfrom(sockfd,recv_packet,sizeof(recv_packet),0,(struct sockaddr*)&from,&fromlen))<0) //收資料 { continue; } if(unpack(recv_packet,n)==-1) //解包失敗 { i++; continue; } else { end++; //成功 } } } //校驗和計算 unsigned short get_cksum(unsigned char* data,int len) //求檢驗和 { int sum=0; int odd=(len&0x01); while(len&0xfffe) { sum+=*(unsigned short*)data; data+=2; len-=2; } if(odd) { unsigned short tmp=((*data)<<8)&0xff00; sum+=tmp; } sum=(sum>>16)+(sum&0xffff); sum+=(sum>>16); return ~sum; } //構建包 int pack(int pack_no) { int size; struct icmp* myicmp; myicmp=(struct icmp*)send_packet; //對相應欄位填充 myicmp->icmp_type=ICMP_ECHO; myicmp->icmp_code=0; myicmp->icmp_cksum=0; myicmp->icmp_seq=pack_no; myicmp->icmp_id=pid; size=8+datalen; //總的icmp大小 //將時間資訊放入data欄位 struct timeval*send_time=(struct timeval*)myicmp->icmp_data; gettimeofday(send_time,NULL); //注意檢驗和放最後,因為資料也要進行校驗 myicmp->icmp_cksum=get_cksum(send_packet,size); return size; } //拆解包 int unpack(char* buf,int len) { int ip_len; struct ip* myip; struct icmp* myicmp; myip=(struct ip*)buf; ip_len=myip->ip_hl<<2; //IP首部長度*4 myicmp=(struct icmp*)(buf+ip_len); //獲得ICMP報文 len-=ip_len; if(len<8) //ICMP固定長度為8,小於即錯誤 { printf("icmp packet is small than 8\n"); return -1; } // printf("i am coming\n"); if(myicmp->icmp_type==ICMP_ECHOREPLY && myicmp->icmp_id==pid) //判斷是不是想要的包 { // printf("i am coming\n"); gettimeofday(&recv_time,NULL); struct timeval* sendTime=(struct timeval*)myicmp->icmp_data; time_sub(&recv_time,sendTime); //計算時間差放到recv_time裡面 double rtt=recv_time.tv_sec*1000+recv_time.tv_usec/1000; //以ms為單位 //列印相關資訊 printf("%d byte from %s:icmp_seq=%u ttl=%d rtt=%fms\n",len,inet_ntoa(from.sin_addr),myicmp->icmp_seq,myip->ip_ttl,rtt); } else //不是想要的包 return -1; } //時間差計算 void time_sub(struct timeval* end,struct timeval* begin) { end->tv_sec-=begin->tv_sec; end->tv_usec-=begin->tv_usec; } //主函式 int main(int argc,char* argv[]) { if(argc<2) { printf("usage:%s hostname/ip\n",argv[0]); exit(-1); } if((sockfd=socket(AF_INET,SOCK_RAW,IPPROTO_ICMP))<0) //建立原始套接字 { perror("socket"); exit(-1); } memset(&from,0,sizeof(from)); if((from.sin_addr.s_addr=inet_addr(argv[1]))==INADDR_NONE)//如果傳入的不是ip { struct hostent* hptr=NULL; //轉換失敗 if((hptr=gethostbyname(argv[1]))==NULL) { perror("gethostbyname"); exit(-1); } memcpy(&from.sin_addr,hptr->h_addr,sizeof(hptr->h_addr)); } else { from.sin_addr.s_addr=inet_addr(argv[1]); } from.sin_family=AF_INET; pid=getpid(); printf("ping %s,%d data in icmp packets\n",argv[1],datalen); pthread_t id1,id2; //建立傳送函式執行緒 if(pthread_create(&id1,NULL,send_data,NULL)<0) { perror("pthread_create"); return -2; } //建立接收函式執行緒 if(pthread_create(&id2,NULL,recv_data,NULL)<0) { perror("pthread_create"); return -3; } //回收執行緒,防止執行緒洩露 pthread_join(id1,NULL); pthread_join(id2,NULL); close(sockfd); printf("ping %s is complete,%d was sended and %d was received\n",argv[1],begin,end); return 0; }

編譯並執行:

gcc -o pingbyme pingbyme.c -lpthread
sudo ./pingbyme www.baidu.com

執行結構展示:

我的ping展示:
在這裡插入圖片描述

linux自帶的ping:
在這裡插入圖片描述