用C語言實現Ping程式功能(轉)
用C語言實現Ping程式功能
日期:2006-12-25 作者:樑俊輝 來自:IBM DW中國
大部分人用ping命令只是作為檢視另一個系統的網路連線是否正常的一種簡單方法。在這篇文章中,作者將介紹如何用C語言編寫一個模擬ping命令功能的程式。
ping命令是用來檢視網路上另一個主機系統的網路連線是否正常的一個工具。ping命令的工作原理是:向網路上的另一個主機系統傳送ICMP報文,如果指定系統得到了報文,它將把報文一模一樣地傳回給傳送者,這有點象潛水艇聲納系統中使用的發聲裝置。
例如,在Linux終端上執行ping localhost命令將會看到以下結果:
|
由上面的執行結果可以看到,ping命令執行後顯示出被測試系統主機名和相應IP地址、返回給當前主機的ICMP報文順序號、ttl生存時間和往返時間rtt(單位是毫秒,即千分之一秒)。要寫一個模擬ping命令,這些資訊有啟示作用。
要真正瞭解ping命令實現原理,就要了解ping命令所使用到的TCP/IP協議。
ICMP(Internet Control Message,網際控制報文協議)是為閘道器和目標主機而提供的一種差錯控制機制,使它們在遇到差錯時能把錯誤報告給報文源發方。ICMP協議是IP層的一個協議,但是由於差錯報告在傳送給報文源發方時可能也要經過若干子網,因此牽涉到路由選擇等問題,所以ICMP報文需通過IP協議來發送。ICMP資料報的資料傳送前需要兩級封裝:首先新增ICMP報頭形成ICMP報文,再新增IP報頭形成IP資料報。如下圖所示
IP報頭 |
ICMP報頭 |
ICMP資料報 |
由於IP層協議是一種點對點的協議,而非端對端的協議,它提供無連線的資料報服務,沒有埠的概念,因此很少使用bind()和connect()函式,若有使用也只是用於設定IP地址。傳送資料使用sendto()函式,接收資料使用recvfrom()函式。IP報頭格式如下圖:
在Linux中,IP報頭格式資料結構()定義如下:
|
其中ping程式只使用以下資料:
- IP報頭長度IHL(Internet Header Length)�D�D以4位元組為一個單位來記錄IP報頭的長度,是上述IP資料結構的ip_hl變數。
- 生存時間TTL(Time To Live)�D�D以秒為單位,指出IP資料報能在網路上停留的最長時間,其值由傳送方設定,並在經過路由的每一個節點時減一,當該值為0時,資料報將被丟棄,是上述IP資料結構的ip_ttl變數。
ICMP報文分為兩種,一是錯誤報告報文,二是查詢報文。每個ICMP報頭均包含型別、編碼和校驗和這三項內容,長度為8位,8位和16位,其餘選項則隨ICMP的功能不同而不同。
Ping命令只使用眾多ICMP報文中的兩種:"請求回送'(ICMP_ECHO)和"請求迴應'(ICMP_ECHOREPLY)。在Linux中定義如下:
|
這兩種ICMP型別報頭格式如下:
在Linux中ICMP資料結構()定義如下:
|
使用巨集定義令表達更簡潔,其中ICMP報頭為8位元組,資料報長度最大為64K位元組。
- 校驗和演算法�D�D這一演算法稱為網際校驗和演算法,把被校驗的資料16位進行累加,然後取反碼,若資料位元組長度為奇數,則資料尾部補一個位元組的0以湊成偶數。此演算法適用於IPv4、ICMPv4、IGMPV4、ICMPv6、UDP和TCP校驗和,更詳細的資訊請參考RFC1071,校驗和欄位為上述ICMP 資料結構的icmp_cksum變數。
- 識別符號�D�D用於唯一標識ICMP報文, 為上述ICMP資料結構的icmp_id巨集所指的變數。
- 順序號�D�Dping命令的icmp_seq便由這裡讀出,代表ICMP報文的傳送順序,為上述ICMP資料結構的icmp_seq巨集所指的變數。
Ping命令中需要顯示的資訊,包括icmp_seq和ttl都已有實現的辦法,但還缺rtt往返時間。為了實現這一功能,可利用ICMP資料報攜帶一個時間戳。使用以下函式生成時間戳:
|
其中tv_sec為秒數,tv_usec微秒數。在傳送和接收報文時由gettimeofday分別生成兩個timeval結構,兩者之差即為往返時間,即 ICMP報文傳送與接收的時間差,而timeval結構由ICMP資料報攜帶,tzp指標表示時區,一般都不使用,賦NULL值。
系統自帶的ping命令當它接送完所有ICMP報文後,會對所有傳送和所有接收的ICMP報文進行統計,從而計算ICMP報文丟失的比率。為達此目的,定義兩個全域性變數:接收計數器和傳送計數器,用於記錄ICMP報文接受和傳送數目。丟失數目=傳送總數-接收總數,丟失比率=丟失數目/傳送總數。
現給出模擬Ping程式功能的程式碼如下:
|
只有root使用者才能利用socket()函式生成原始套接字,要讓Linux的一般使用者能執行以上程式,需進行如下的特別操作:
用root登陸,編譯以上程式:gcc -o myping myping.c,其目的有二:一是編譯,二是讓myping屬於root使用者。
再執行chmod u+s myping,目的是把myping程式設成SUID的屬性。
退出root,用一般使用者登陸,執行./myping www.cn.ibm.com,有以下執行結果:
|
由於myping.c是傳送完所有的ICMP報文才去接收,因此第一、第二和第三個ICMP報文的往返時間依此是3秒,2秒,1秒,上述結果中rtt資訊正反映這一事實。