1. 程式人生 > >在linux中用C語言實現ping命令

在linux中用C語言實現ping命令

運用C語言編寫模擬常用網路命令ping命令實現一個基於Linux原始套接字和ICMP協議的ping程式。該程式能用於檢測主機或路由器工作是否正常。

程式中主要的函式

void alarm_handler(int); /*SIGALRM處理程式*/

void int_handler(int); /*SIGINT處理程式*/

void set_sighandler(); /*設定訊號處理程式*/

void send_ping(); /*傳送ping訊息*/

void recv_reply(); /*接收ping應答*/

u16 checksum(u8 *buf, int len); /*計算校驗和*/

int handle_pkt(); /*ICMP應答訊息處理*/

void get_statistics(int, int); /*統計ping命令的檢測結果*/

void bail(const char *); /*錯誤報告*/

ping.h檔案:

/********************************************************************************
 *      Copyright:  (C) 2016 Yang Zheng<[email protected]>
 *                  All rights reserved.
 *
 *       Filename:  ping.h
 *    Description:  This head file 
 *
 *        Version:  1.0.0(01/22/2016~)
 *         Author:  Yang Zheng <
[email protected]
> * ChangeLog: 1, Release initial version on "01/22/2016 04:51:41 PM" * ********************************************************************************/</span> #define ICMP_ECHOREPLY 0 /* Echo應答*/ #define ICMP_ECHO /*Echo請求*/ #define BUFSIZE 1500 /*傳送快取最大值*/ #define DEFAULT_LEN 56 /**ping訊息資料預設大小/ /*資料類型別名*/ typedef unsigned char u8; typedef unsigned short u16; typedef unsigned int u32; /*ICMP訊息頭部*/ struct icmphdr { u8 type; /*定義訊息型別*/ u8 code; /*定義訊息程式碼*/ u16 checksum; /*定義校驗*/ union{ struct{ u16 id; u16 sequence; }echo; u32 gateway; struct{ u16 unsed; u16 mtu; }frag; /*pmtu實現*/ }un; /*ICMP資料佔位符*/ u8 data[0]; #define icmp_id un.echo.id #define icmp_seq un.echo.sequence }; #define ICMP_HSIZE sizeof(struct icmphdr) /*定義一個IP訊息頭部結構體*/ struct iphdr { u8 hlen:4, ver:4; /*定義4位首部長度,和IP版本號為IPV4*/ u8 tos; /*8位服務型別TOS*/ u16 tot_len; /*16位總長度*/ u16 id; /*16位標誌位*/ u16 frag_off; /*3位標誌位*/ u8 ttl; /*8位生存週期*/ u8 protocol; /*8位協議*/ u16 check; /*16位IP首部校驗和*/ u32 saddr; /*32位源IP地址*/ u32 daddr; /*32位目的IP地址*/ }; char *hostname; /*被ping的主機名*/ int datalen = DEFAULT_LEN; /*ICMP訊息攜帶的資料長度*/ char sendbuf[BUFSIZE]; /*傳送字串陣列*/ char recvbuf[BUFSIZE]; /*接收字串陣列*/ int nsent; /*傳送的ICMP訊息序號*/ int nrecv; /*接收的ICMP訊息序號*/ pid_t pid; /*ping程式的程序PID*/ struct timeval recvtime; /*收到ICMP應答的時間戳*/ int sockfd; /*傳送和接收原始套接字*/ struct sockaddr_in dest; /*被ping的主機IP*/ struct sockaddr_in from; /*傳送ping應答訊息的主機IP*/ struct sigaction act_alarm; struct sigaction act_int; /*函式原型*/ void alarm_handler(int); /*SIGALRM處理程式*/ void int_handler(int); /*SIGINT處理程式*/ void set_sighandler(); /*設定訊號處理程式*/ void send_ping(); /*傳送ping訊息*/ void recv_reply(); /*接收ping應答*/ u16 checksum(u8 *buf, int len); /*計算校驗和*/ int handle_pkt(); /*ICMP應答訊息處理*/ void get_statistics(int, int); /*統計ping命令的檢測結果*/ void bail(const char *); /*錯誤報告*/


ping.c檔案:

/*********************************************************************************
 *      Copyright:  (C) 2016 Yang Zheng<[email protected]>  
 *                  All rights reserved.
 *
 *       Filename:  ping.c
 *    Description:  This file 
 *                 
 *        Version:  1.0.0(01/22/2016~)
 *         Author:  Yang Zheng <[email protected]>
 *      ChangeLog:  1, Release initial version on "01/22/2016 04:51:12 PM"
 *                 
 ********************************************************************************/</span>
#include<stdio.h>  
#include<stdlib.h>  
#include<sys/time.h>  /*是Linux系統的日期時間標頭檔案*/  
#include<unistd.h>    /* 是POSIX標準定義的unix類系統定義符號常量的標頭檔案,包含了許多UNIX系統服務的函式原型,例如read函式、write函式和getpid函式*/  
#include<string.h>  
#include<sys/socket.h>    /*對與引用socket函式必須*/  
#include<sys/types.h>  
#include<netdb.h> /*定義了與網路有關的結構,變數型別,巨集,函式。函式gethostbyname()用*/  
#include<errno.h> /*sys/types.h中文名稱為基本系統資料型別*/  
#include<arpa/inet.h> /*inet_ntoa()和inet_addr()這兩個函式,包含在 arpa/inet.h*/  
#include<signal.h>    /*程序對訊號進行處理*/  
#include<netinet/in.h>    /*網際網路地址族*/  
  
#include"ping.h"  
#define IP_HSIZE sizeof(struct iphdr)   /*定義IP_HSIZE為ip頭部長度*/  
#define IPVERSION  4   /*定義IPVERSION為4,指出用ipv4*/  

/*設定的時間是一個結構體,倒計時設定,重複倒時,超時值設為1秒*/  
struct itimerval val_alarm = {
	.it_interval.tv_sec = 1,      
	.it_interval.tv_usec = 0,  
	.it_value.tv_sec = 0,  
	.it_value.tv_usec = 1  
};  

/*argc表示隱形程式命令列中引數的數目,argv是一個指向字串陣列指標,其中每一個字元對應一個引數*/
int main(int argc,char **argv)  
{  
	struct hostent		*host; /*該結構體屬於include<netdb.h>*/   
    int					on = 1;  
  
    if( argc < 2)/*判斷是否輸入了地址*/ 
	{       
		printf("Usage: %s hostname\n",argv[0]);  
		exit(1);  
    }  

	/*gethostbyname()返回對應於給定主機名的包含主機名字和地址資訊的結構指標,*/ 
    //if((host = getaddrinfo(argv[1])) == NULL)
    if((host = gethostbyname(argv[1])) == NULL)
	{     
		printf("usage:%s hostname/IP address\n", argv[0]);
		exit(1);  
    }  
  
    hostname = argv[1];	/*取出地址名*/  
  
	memset(&dest,0,sizeof dest);	/*將dest中前sizeof(dest)個位元組替換為0並返回s,此處為初始化,給最大記憶體清零*/  
	dest.sin_family=PF_INET;		/*PF_INET為IPV4,internet協議,在<netinet/in.h>中,地址族*/   
	dest.sin_port=ntohs(0);			/*埠號,ntohs()返回一個以主機位元組順序表達的數。*/  
	dest.sin_addr=*(struct in_addr *)host->h_addr_list[0];/*host->h_addr_list[0]是地址的指標.返回IP地址,初始化*/  

	/*PF_INEI套接字協議族,SOCK_RAW套接字型別,IPPROTO_ICMP使用協議,
	呼叫socket函式來建立一個能夠進行網路通訊的套接字。這裡判斷是否建立成功*/ 
	if((sockfd = socket(PF_INET, SOCK_RAW, IPPROTO_ICMP)) < 0)
	{  
		perror("RAW socket created error");  
		exit(1);  
    }  

	/*設定當前套接字選項特定屬性值,sockfd套接字,IPPROTO_IP協議層為IP層,
	IP_HDRINCL套接字選項條目,套接字接收緩衝區指標,sizeof(on)緩衝區長度的長度*/ 
    setsockopt(sockfd, IPPROTO_IP, IP_HDRINCL, &on, sizeof(on));   

	/*getuid()函式返回一個呼叫程式的真實使用者ID,setuid()是讓普通使用者
	可以以root使用者的角色執行只有root帳號才能執行的程式或命令。*/ 
	setuid(getuid()); 
	pid = getpid(); /*getpid函式用來取得目前程序的程序識別碼*/  
  
	set_sighandler();/*對訊號處理*/  
	printf("Ping %s(%s): %d bytes data in ICMP packets.\n", argv[1], inet_ntoa(dest.sin_addr), datalen);  
  
	if((setitimer(ITIMER_REAL, &val_alarm, NULL)) == -1) /*定時函式*/  
	{
        bail("setitimer fails.");  
	}
  
    recv_reply(); /*接收ping應答*/  
  
	return 0;  
}  

/*傳送ping訊息*/  
void send_ping(void)  
{  
    struct iphdr		*ip_hdr;   /*iphdr為IP頭部結構體*/  
    struct icmphdr		*icmp_hdr;   /*icmphdr為ICMP頭部結構體*/  
    int					len;  
    int					len1;  

	/*ip頭部結構體變數初始化*/  
    ip_hdr=(struct iphdr *)sendbuf; /*字串指標*/     
    ip_hdr->hlen=sizeof(struct iphdr)>>2;  /*頭部長度*/  
    ip_hdr->ver=IPVERSION;   /*版本*/  
    ip_hdr->tos=0;   /*服務型別*/  
    ip_hdr->tot_len=IP_HSIZE+ICMP_HSIZE+datalen; /*報文頭部加資料的總長度*/  
    ip_hdr->id=0;    /*初始化報文標識*/  
    ip_hdr->frag_off=0;  /*設定flag標記為0*/  
    ip_hdr->protocol=IPPROTO_ICMP;/*運用的協議為ICMP協議*/  
    ip_hdr->ttl=255; /*一個封包在網路上可以存活的時間*/  
    ip_hdr->daddr=dest.sin_addr.s_addr;  /*目的地址*/  
    len1=ip_hdr->hlen<<2;  /*ip資料長度*/  
    /*ICMP頭部結構體變數初始化*/  
    icmp_hdr=(struct icmphdr *)(sendbuf+len1);  /*字串指標*/  
    icmp_hdr->type=8;    /*初始化ICMP訊息型別type*/  
    icmp_hdr->code=0;    /*初始化訊息程式碼code*/  
    icmp_hdr->icmp_id=pid;   /*把程序標識碼初始給icmp_id*/  
    icmp_hdr->icmp_seq=nsent++;  /*傳送的ICMP訊息序號賦值給icmp序號*/      
    memset(icmp_hdr->data,0xff,datalen);  /*將datalen中前datalen個位元組替換為0xff並返回icmp_hdr-dat*/    
  
    gettimeofday((struct timeval *)icmp_hdr->data,NULL); /* 獲取當前時間*/  
  
    len=ip_hdr->tot_len; /*報文總長度賦值給len變數*/  
    icmp_hdr->checksum=0;    /*初始化*/  
    icmp_hdr->checksum=checksum((u8 *)icmp_hdr,len);  /*計算校驗和*/  
  
    sendto(sockfd,sendbuf,len,0,(struct sockaddr *)&dest,sizeof (dest)); /*經socket傳送資料*/  
}  

/*接收程式發出的ping命令的應答*/  
void recv_reply()  
{  
	int			n;  
	int			len;  
    int			errno;  
  
    n = 0;
	nrecv = 0;  
    len = sizeof(from);   /*傳送ping應答訊息的主機IP*/  
  
    while(nrecv < 4)
	{  
		/*經socket接收資料,如果正確接收返回接收到的位元組數,失敗返回0.*/
		if((n=recvfrom(sockfd, recvbuf, sizeof(recvbuf), 0, (struct sockaddr *)&from, &len))<0)
		{   
			if(errno==EINTR)  /*EINTR表示訊號中斷*/  
				continue;  
            bail("recvfrom error");  
        }  
  
		gettimeofday(&recvtime, NULL);   /*記錄收到應答的時間*/  

		if(handle_pkt())    /*接收到錯誤的ICMP應答資訊*/  
			continue;  

		nrecv++;  
    }  
  
    get_statistics(nsent, nrecv);     /*統計ping命令的檢測結果*/  
}  

 /*計算校驗和*/  
u16 checksum(u8 *buf,int len)  
{  
    u32 sum	= 0;  
    u16 *cbuf;  
  
    cbuf = (u16 *)buf;  
  
    while(len > 1)
	{  
		sum += *cbuf++;  
		len -= 2;  
    }  
  
    if(len)
	{
        sum += *(u8 *)cbuf;  
	}
  
	sum = (sum >> 16) + (sum & 0xffff);  
	sum += (sum >> 16);  

	return ~sum;  
}  

/*ICMP應答訊息處理*/  
int handle_pkt()  
{  
	struct iphdr		*ip;  
    struct icmphdr		*icmp;  
    int					ip_hlen;  
    u16					ip_datalen; /*ip資料長度*/  
    double				rtt; /* 往返時間*/  
    struct timeval		*sendtime;  
  
    ip = (struct iphdr *)recvbuf;  
  
    ip_hlen = ip->hlen << 2;  
    ip_datalen = ntohs(ip->tot_len) - ip_hlen;  
  
    icmp = (struct icmphdr *)(recvbuf + ip_hlen);  
  
    if(checksum((u8 *)icmp, ip_datalen)) /*計算校驗和*/  
       return -1;  
  
	if(icmp->icmp_id != pid)  
		return -1;  

	sendtime = (struct timeval *)icmp->data; /*傳送時間*/  
	rtt = ((&recvtime)->tv_sec - sendtime->tv_sec) * 1000 + ((&recvtime)->tv_usec - sendtime->tv_usec)/1000.0; /* 往返時間*/  
	/*列印結果*/  
	printf("%d bytes from %s:icmp_seq=%u ttl=%d rtt=%.3f ms\n",  \
			ip_datalen,					/*IP資料長度*/  
			inet_ntoa(from.sin_addr),   /*目的ip地址*/  
			icmp->icmp_seq,				/*icmp報文序列號*/  
			ip->ttl,					/*生存時間*/  
			rtt);						/*往返時間*/  

	return 0;  
}  

/*設定訊號處理程式*/  
void set_sighandler()  
{  
	act_alarm.sa_handler = alarm_handler;  
	/*sigaction()會依引數signum指定的訊號編號來設定該訊號的處理函式。引數signum指所要捕獲訊號或忽略的訊號,
	&act代表新設定的訊號共用體,NULL代表之前設定的訊號處理結構體。這裡判斷對訊號的處理是否成功。*/
    if(sigaction(SIGALRM, &act_alarm, NULL) == -1)    
	{
		bail("SIGALRM handler setting fails.");  
	}
  
	act_int.sa_handler = int_handler;  
    if(sigaction(SIGINT, &act_int, NULL) == -1)  
	{
		bail("SIGALRM handler setting fails.");  
	}
}  

 /*統計ping命令的檢測結果*/  
void get_statistics(int nsent,int nrecv)  
{  
    printf("--- %s ping statistics ---\n",inet_ntoa(dest.sin_addr)); /*將網路地址轉換成“.”點隔的字串格式。*/  
    printf("%d packets transmitted, %d received, %0.0f%% ""packet loss\n",  \
		nsent,nrecv,1.0*(nsent-nrecv)/nsent*100);  
}  

/*錯誤報告*/  
void bail(const char * on_what)  
{  
	/*:向指定的檔案寫入一個字串(不寫入字串結束標記符‘\0’)。成功寫入一個字串後,
	檔案的位置指標會自動後移,函式返回值為0;否則返回EOR(符號常量,其值為-1)。*/ 
    fputs(strerror(errno),stderr);   
    fputs(":",stderr);  
    fputs(on_what,stderr);  
    fputc('\n',stderr); /*送一個字元到一個流中*/  

    exit(1);  
}  
  
 /*SIGINT(中斷訊號)處理程式*/  
void int_handler(int sig)  
{  
    get_statistics(nsent,nrecv);    /*統計ping命令的檢測結果*/  
    close(sockfd);  /*關閉網路套接字*/  
    exit(1);  
}  

 /*SIGALRM(終止程序)處理程式*/  
void alarm_handler(int signo)  
{  
    send_ping();    /*傳送ping訊息*/  
  
}<strong>
</strong>

執行結果:

# ./ping 192.168.1.1
Ping 192.168.1.1(192.168.1.1): 56 bytes data in ICMP packets.
64 bytes from 192.168.1.1:icmp_seq=0 ttl=64 rtt=113.510 ms
64 bytes from 192.168.1.1:icmp_seq=1 ttl=64 rtt=19.854 ms
--- 192.168.1.1 ping statistics ---
2 packets transmitted, 2 received, 0% packet loss

# ./ping baidu.com
Ping baidu.com(220.181.57.217): 56 bytes data in ICMP packets.
64 bytes from 220.181.57.217:icmp_seq=0 ttl=50 rtt=44.679 ms
64 bytes from 220.181.57.217:icmp_seq=1 ttl=50 rtt=58.446 ms
64 bytes from 220.181.57.217:icmp_seq=2 ttl=50 rtt=47.933 ms
64 bytes from 220.181.57.217:icmp_seq=3 ttl=50 rtt=44.946 ms
--- 220.181.57.217 ping statistics ---
4 packets transmitted, 4 received, 0% packet loss

相關推薦

linux中用C語言實現ping命令

運用C語言編寫模擬常用網路命令ping命令實現一個基於Linux原始套接字和ICMP協議的ping程式。該程式能用於檢測主機或路由器工作是否正常。 程式中主要的函式 void alarm_handler(int); /*SIGALRM處理程式*/ void int_h

Linux下:用 C 語言實現 ls 命令

這次主要的目的是用 C語言 實現 Linux 系統中的 ls 命令。 在編寫命令之前,需要介紹一下幾個結構體: 第一個 DIR: struct __dirstream { void *__fd; char *__data; in

c語言實現cp命令

#include<stdio.h> #include<unistd.h> #include<errno.h> #include<sys/types.h> #include<sys/stat.h> #include<fcntl.

c語言 實現ping 功能/效果 檢測當前網路是否連通

#include<stdio.h> #include <unistd.h> int go_ping(char *svrip) {         int i = 0;         whil

linux系統C語言實現域名解析功能

版權宣告:本文為遲思堂主人李遲原創文章,版權所有。可隨便任意使用(包括學習研究商用),但由此帶來的成果或後果,概與作者無關。胡亂修改的,不註明出處的,概不負責。 https://blog.csdn.net/subfate/article/details/81776147 背景 後臺專案劃分幾個小

C語言實現Ping程式功能(轉)

用C語言實現Ping程式功能 日期:2006-12-25 作者:樑俊輝 來自:IBM DW中國 大部分人用ping命令只是作為檢視另一個系統的網路連線是否正常的一種簡單方法。在這篇文章中,作者將介紹如何用C語言編寫一個模擬ping命令功能的程式。 ping命令是用來檢視網路

LinuxC語言實現資料夾拷貝

在《【Linux】利用C語言檔案流複製單一檔案》(點選開啟連結)講述瞭如何用C語言拷貝檔案,但是這隻能拷貝單一檔案。如果你要用LinuxC拷貝整個資料夾,同樣要像《【Java】利用檔案輸入輸出流完成把一個資料夾內的所有檔案拷貝的另一的資料夾的操作》(點選開啟連結)一樣,先用

LinuxC語言實現獲取當前時間

C語言獲取當前時間 簡介 在工作中,經常涉及到獲取當前時間,用於寫日誌,基於此,今特意利用C語言寫一個獲取時間函式,用於後面用到時,能夠及時查到。獲取當前時間,要用到time.h中的time()和localtime()函式,二者具體介紹與使用,參見 ht

linuxC語言實現udp

目的:C語言實現udp_client 和udp_server功能,實現簡單的資料收發功能;參考出處:1、https://www.cnblogs.com/yuqiao/p/5786427.html //socket介面詳解2、https://blog.csdn.net/zgrj

LinuxC語言實現C/S模式程式設計(附原始碼,執行截圖)

由標題可知,這篇部落格主要講如何用C語言實現一個C/S模式的程式。 主要功能:時間回送。 客戶機發出請求,伺服器響應時間,並返回伺服器時間,與客戶機進行同步。 廢話不多說,下面直接貼出原始碼。 程式碼如下: #include <stdio.h> #include

linuxC語言實現求CPU利用率

intr 21217894 119 18974 0 0 0 0 0 1 1 0 0 0 0 0 0 0 0 146350 0 647836 370 86696 3 146156 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 

linuxC語言實現生產者消費者問題

問題描述: 緩衝區大小為N,生產者產生物品放到緩衝區,消費者將物品從緩衝區移走 互斥關係: 對緩衝區得訪問需要互斥,包括生產者和生產者之間、消費者和消費者之間、生產者和消費者之間。 同步關係:

Linuxc語言實現進度條

顏色的處理 要想把進度條加上顏色,我們只需要在需要顯示顏色的部分輸入顏色編號即可,但是要在最後用\033[0m關閉所有屬性,不然程式執行後會在執行後的後續文字中也變為我們之前所設定的顏色。 通過控制符即可設定我們需要的顏色: \033[30m– \033[

linux whoami cp mv mesg 命令 c語言實現

whoami實現 #include<stdio.h>#include<pwd.h>#include<sys/types.h>#include<unistd.h>int main(){ uid_t id; struc

linuxPing工具的C語言實現

前言:     ping命令是用來檢視網路上另一個主機系統的網路連線是否正常的一個工具     ping命令的工作原理是:向網路上的另一個主機系統傳送ICMP報文,如果指定系統得到了報文,它將把報文一模一樣地傳回給傳送者 ping實現的九大步驟:       第一步:建立i

C語言實現linux pwd命令

實驗內容 通過實現Linux的pwd命令,以理解Linux檔案系統的基本概念一集內部實現,並熟悉Linux系統與檔案系統相關的系統呼叫介面。 知識點: 列表內容 Linux pwd命令 Linux檔案系統中檔案及目錄的實現方式 Linux檔

Linux下的ls命令詳解以及C語言實現

一、眾所周知,ls是linux下最常用的命令之一,使用起來也相當的快捷與方便,ls 命令將每個由 Directory 引數指定的目錄或者每個由 File 引數指定的名稱寫到標準輸出,以及您所要求的和標誌一起的其它資訊。如果不指定 File 或 Directory

Linux終端程序用c語言實現改變輸出的字的顏色

光標位置 高亮 AI 藍色 屬性 用c語言實現 TE c語言 說明 顏色代碼: 格式: echo "\033[字背景顏色;字體顏色m字符串\033[0m" 例如: echo "\033[41;36m something here \033[0m" 其中41的位置代表

c語言實現linux下高危函式system (簡易V1.0版本)

system這個函式真的是要慎用,一不小心就會留下漏洞。 下面是用c語言簡易的實現了一下system函式 #include<stdio.h> #include<stdlib.h> #include<unistd.h> #include<err

(排序演算法)linux c語言實現選擇排序演算法(氣泡排序的略微改進版)

 快速排序演算法和氣泡排序演算法是差不多的,都是要兩層迴圈,外迴圈是要比較的個數,其實就是元素的個數,內迴圈就是外層那個標記和其他的比較大小, 氣泡排序是相鄰的兩個,兩兩比較,最後交換出一個最大或者最小值, 快速排序是在氣泡排序的基礎上,找出那個最小的或者最大的,但是不是直接交換,