PING(Packet InterNet Groper)中文名為因特網(wǎng)包探索器,是用來查看網(wǎng)絡(luò)上另一個(gè)主機(jī)系統(tǒng)的網(wǎng)絡(luò)連接是否正常的一個(gè)工具。ping命令的工作原理是:向網(wǎng)絡(luò)上的另一個(gè)主機(jī)系統(tǒng)發(fā)送ICMP報(bào)文,如果指定系統(tǒng)得到了報(bào)文,它將把回復(fù)報(bào)文傳回給發(fā)送者,這有點(diǎn)象潛水艇聲納系統(tǒng)中使用的發(fā)聲裝置。所以,我們想知道我這臺(tái)主機(jī)能不能和另一臺(tái)進(jìn)行通信,我們首先需要確認(rèn)的是我們兩臺(tái)主機(jī)間的網(wǎng)絡(luò)是不是通的,也就是我說的話能不能傳到你那里,這是雙方進(jìn)行通信的前提。在Linux下使用指令ping的方法和現(xiàn)象如下:
PING的實(shí)現(xiàn)看起來并不復(fù)雜,我想自己寫代碼實(shí)現(xiàn)這個(gè)功能,需要些什么知識(shí)儲(chǔ)備?我簡(jiǎn)單羅列了一下:
·ICMP協(xié)議的理解
·RAW套接字
·網(wǎng)絡(luò)封包和解包技能
搭建這么一個(gè)ping程序的步驟如下:
1、ICMP包的封裝和解封
2、創(chuàng)建一個(gè)線程用于ICMP包的發(fā)送
3、創(chuàng)建一個(gè)線程用于ICMP包的接收
4、原始套接字編程
PING的流程如下:
一、ICMP包的封裝和解封
(1) ICMP協(xié)議理解要進(jìn)行PING的開發(fā),我們首先需要知道PING的實(shí)現(xiàn)是基于ICMP協(xié)議來開發(fā)的。要進(jìn)行ICMP包的封裝和解封,我們首先需要理解ICMP協(xié)議。ICMP位于網(wǎng)絡(luò)層,允許主機(jī)或者路由器報(bào)告差錯(cuò)情況和提供有關(guān)異常情況的報(bào)告。ICMP報(bào)文是封裝在IP數(shù)據(jù)報(bào)中,作為其中的數(shù)據(jù)部分。ICMP報(bào)文作為IP層數(shù)據(jù)報(bào)的數(shù)據(jù),加上數(shù)據(jù)報(bào)頭,組成IP數(shù)據(jù)報(bào)發(fā)送出去。ICMP報(bào)文格式如下:
ICMP報(bào)文的種類有兩種,即ICMP差錯(cuò)報(bào)告報(bào)文和ICMP詢問報(bào)文。PING程序使用的ICMP報(bào)文種類為ICMP詢問報(bào)文。注意一下上面說到的ICMP報(bào)文格式中的“類型”字段,我們?cè)诮M包的時(shí)候可以向該字段填寫不同的值來標(biāo)定該ICMP報(bào)文的類型。下面列出的是幾種常用的ICMP報(bào)文類型。
我們的PING程序需要用到的ICMP的類型是回送請(qǐng)求(8)。
因?yàn)镮CMP報(bào)文的具體格式會(huì)因?yàn)镮CMP報(bào)文的類型而各不相同,我們ping包的格式是這樣的:
(2) ICMP包的組裝
對(duì)照上面的ping包格式,我們封裝ping包的代碼可以這么寫:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
void icmp_pack(struct icmp* icmphdr, int seq, int length) { int i = 0 ; icmphdr->icmp_type = ICMP_ECHO; //類型填回送請(qǐng)求 icmphdr->icmp_code = 0 ; icmphdr->icmp_cksum = 0 ; //注意,這里先填寫0,很重要! icmphdr->icmp_seq = seq; //這里的序列號(hào)我們填1,2,3,4.... icmphdr->icmp_id = pid & 0xffff ; //我們使用pid作為icmp_id,icmp_id只是2字節(jié),而pid有4字節(jié) for (i= 0 ;i<length;i++) { icmphdr->icmp_data[i] = i; //填充數(shù)據(jù)段,使ICMP報(bào)文大于64B } icmphdr->icmp_cksum = cal_chksum((unsigned short *)icmphdr, length); //校驗(yàn)和計(jì)算 } |
這里再三提醒一下,icmp_cksum 必須先填寫為0再執(zhí)行校驗(yàn)和算法計(jì)算,否則ping時(shí)對(duì)方主機(jī)會(huì)因?yàn)樾r?yàn)和計(jì)算錯(cuò)誤而丟棄請(qǐng)求包,導(dǎo)致ping的失敗。我一個(gè)同事曾經(jīng)就因?yàn)檫@么一個(gè)錯(cuò)誤而排查許久,血的教訓(xùn)請(qǐng)銘記。
這里簡(jiǎn)單介紹一下checksum(校驗(yàn)和)。
計(jì)算機(jī)網(wǎng)絡(luò)通信時(shí),為了檢驗(yàn)在數(shù)據(jù)傳輸過程中數(shù)據(jù)是否發(fā)生了錯(cuò)誤,通常在傳輸數(shù)據(jù)的時(shí)候連同校驗(yàn)和一塊傳輸,當(dāng)接收端接受數(shù)據(jù)時(shí)候會(huì)從新計(jì)算校驗(yàn)和,如果與原校驗(yàn)和不同就視為出錯(cuò),丟棄該數(shù)據(jù)包,并返回icmp報(bào)文。
算法基本思路:
IP/ICMP/IGMP/TCP/UDP等協(xié)議的校驗(yàn)和算法都是相同的,采用的都是將數(shù)據(jù)流視為16位整數(shù)流進(jìn)行重復(fù)疊加計(jì)算。為了計(jì)算檢驗(yàn)和,首先把檢驗(yàn)和字段置為0。然后,對(duì)有效數(shù)據(jù)范圍內(nèi)中每個(gè)16位進(jìn)行二進(jìn)制反碼求和,結(jié)果存在檢驗(yàn)和字段中,如果數(shù)據(jù)長(zhǎng)度為奇數(shù)則補(bǔ)一字節(jié)0。當(dāng)收到數(shù)據(jù)后,同樣對(duì)有效數(shù)據(jù)范圍中每個(gè)16位數(shù)進(jìn)行二進(jìn)制反碼的求和。由于接收方在計(jì)算過程中包含了發(fā)送方存在首部中的檢驗(yàn)和,因此,如果首部在傳輸過程中沒有發(fā)生任何差錯(cuò),那么接收方計(jì)算的結(jié)果應(yīng)該為全0或全1(具體看實(shí)現(xiàn)了,本質(zhì)一樣) 。如果結(jié)果不是全0或全1,那么表示數(shù)據(jù)錯(cuò)誤。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
/*校驗(yàn)和算法*/ unsigned short cal_chksum(unsigned short *addr, int len) { int nleft=len; int sum= 0 ; unsigned short *w=addr; unsigned short answer= 0 ; /*把ICMP報(bào)頭二進(jìn)制數(shù)據(jù)以2字節(jié)為單位累加起來*/ while (nleft> 1 ) { sum+=*w++; nleft-= 2 ; } /*若ICMP報(bào)頭為奇數(shù)個(gè)字節(jié),會(huì)剩下最后一字節(jié)。把最后一個(gè)字節(jié)視為一個(gè)2字節(jié)數(shù)據(jù)的高字節(jié),這個(gè)2字節(jié)數(shù)據(jù)的低字節(jié)為0,繼續(xù)累加*/ if ( nleft== 1 ) { *(unsigned char *)(&answer)=*(unsigned char *)w; sum+=answer; } sum=(sum>> 16 )+(sum& 0xffff ); sum+=(sum>> 16 ); answer=~sum; return answer; } |
(3) ICMP包的解包
知道怎么封裝包,那解包就也不難了,注意的是,收到一個(gè)ICMP包,我們不要就認(rèn)為這個(gè)包就是我們發(fā)出去的ICMP回送回答包,我們需要加一層代碼來判斷該ICMP報(bào)文的id和seq字段是否符合我們發(fā)送的ICMP報(bào)文的設(shè)置,來驗(yàn)證ICMP回復(fù)包的正確性。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
|
int icmp_unpack( char * buf, int len) { int iphdr_len; struct timeval begin_time, recv_time, offset_time; int rtt; //round trip time struct ip* ip_hdr = (struct ip *)buf; iphdr_len = ip_hdr->ip_hl* 4 ; struct icmp* icmp = (struct icmp*)(buf+iphdr_len); //使指針跳過IP頭指向ICMP頭 len-=iphdr_len; //icmp包長(zhǎng)度 if (len < 8 ) //判斷長(zhǎng)度是否為ICMP包長(zhǎng)度 { fprintf(stderr, "Invalid icmp packet.Its length is less than 8\n" ); return - 1 ; } //判斷該包是ICMP回送回答包且該包是我們發(fā)出去的 if ((icmp->icmp_type == ICMP_ECHOREPLY) && (icmp->icmp_id == pid)) { if ((icmp->icmp_seq < 0 ) || (icmp->icmp_seq > PACKET_SEND_MAX_NUM)) { fprintf(stderr, "icmp packet seq is out of range!\n" ); return - 1 ; } ping_packet[icmp->icmp_seq].flag = 0 ; begin_time = ping_packet[icmp->icmp_seq].begin_time; //去除該包的發(fā)出時(shí)間 gettimeofday(&recv_time, NULL); offset_time = cal_time_offset(begin_time, recv_time); rtt = offset_time.tv_sec* 1000 + offset_time.tv_usec/ 1000 ; //毫秒為單位 printf( "%d byte from %s: icmp_seq=%u ttl=%d rtt=%d ms\n" , len, inet_ntoa(ip_hdr->ip_src), icmp->icmp_seq, ip_hdr->ip_ttl, rtt); } else { fprintf(stderr, "Invalid ICMP packet! Its id is not matched!\n" ); return - 1 ; } return 0 ; } |
二、發(fā)包線程的搭建
根據(jù)PING程序的框架,我們需要建立一個(gè)線程用于ping包的發(fā)送,我的想法是這樣的:使用sendto進(jìn)行發(fā)包,發(fā)包速率我們維持在1秒1發(fā),我們需要用一個(gè)全局變量記錄第一個(gè)ping包發(fā)出的時(shí)間,除此之外,我們還需要一個(gè)全局變量來記錄我們發(fā)出的ping包到底有幾個(gè),這兩個(gè)變量用于后來收到ping包回復(fù)后的數(shù)據(jù)計(jì)算。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
void ping_send() { char send_buf[ 128 ]; memset(send_buf, 0 , sizeof(send_buf)); gettimeofday(&start_time, NULL); //記錄第一個(gè)ping包發(fā)出的時(shí)間 while (alive) { int size = 0 ; gettimeofday(&(ping_packet[send_count].begin_time), NULL); ping_packet[send_count].flag = 1 ; //將該標(biāo)記為設(shè)置為該包已發(fā)送 icmp_pack((struct icmp*)send_buf, send_count, 64 ); //封裝icmp包 size = sendto(rawsock, send_buf, 64 , 0 , (struct sockaddr*)&dest, sizeof(dest)); send_count++; //記錄發(fā)出ping包的數(shù)量 if (size < 0 ) { fprintf(stderr, "send icmp packet fail!\n" ); continue ; } sleep( 1 ); } } |
三、收包線程的搭建
我們同樣建立一個(gè)接收包的線程,這里我們采用select函數(shù)進(jìn)行收包,并為select函數(shù)設(shè)置超時(shí)時(shí)間為200us,若發(fā)生超時(shí),則進(jìn)行下一個(gè)循環(huán)。同樣地,我們也需要一個(gè)全局變量來記錄成功接收到的ping回復(fù)包的數(shù)量。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
|
void ping_recv() { struct timeval tv; tv.tv_usec = 200 ; //設(shè)置select函數(shù)的超時(shí)時(shí)間為200us tv.tv_sec = 0 ; fd_set read_fd; char recv_buf[ 512 ]; memset(recv_buf, 0 ,sizeof(recv_buf)); while (alive) { int ret = 0 ; FD_ZERO(&read_fd); FD_SET(rawsock, &read_fd); ret = select(rawsock+ 1 , &read_fd, NULL, NULL, &tv); switch (ret) { case - 1 : fprintf(stderr, "fail to select!\n" ); break ; case 0 : break ; default : { int size = recv(rawsock, recv_buf, sizeof(recv_buf), 0 ); if (size < 0 ) { fprintf(stderr, "recv data fail!\n" ); continue ; } ret = icmp_unpack(recv_buf, size); //對(duì)接收的包進(jìn)行解封 if (ret == - 1 ) //不是屬于自己的icmp包,丟棄不處理 { continue ; } recv_count++; //接收包計(jì)數(shù) } break ; } } }<strong> </strong> |
四、中斷處理
我們規(guī)定了一次ping發(fā)送的包的最大值為64個(gè),若超出該數(shù)值就停止發(fā)送。作為PING的使用者,我們一般只會(huì)發(fā)送若干個(gè)包,若有這幾個(gè)包順利返回,我們就crtl+c中斷ping。這里的代碼主要是為中斷信號(hào)寫一個(gè)中斷處理函數(shù),將alive這個(gè)全局變量設(shè)置為0,進(jìn)而使發(fā)送ping包的循環(huán)停止而結(jié)束程序。
1
2
3
4
5
6
7
8
|
void icmp_sigint( int signo) { alive = 0 ; gettimeofday(&end_time, NULL); time_interval = cal_time_offset(start_time, end_time); } signal(SIGINT, icmp_sigint); |
五、總體實(shí)現(xiàn)
各模塊介紹完了,現(xiàn)在貼出完整代碼。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
|
#include <stdio.h> #include <netinet/in.h> #include <netinet/ip.h> #include <netinet/ip_icmp.h> #include <unistd.h> #include <signal.h> #include <arpa/inet.h> #include <errno.h> #include <sys/time.h> #include <string.h> #include <netdb.h> #include <pthread.h> #define PACKET_SEND_MAX_NUM 64 typedef struct ping_packet_status { struct timeval begin_time; struct timeval end_time; int flag; //發(fā)送標(biāo)志,1為已發(fā)送 int seq; //包的序列號(hào) }ping_packet_status; ping_packet_status ping_packet[PACKET_SEND_MAX_NUM]; int alive; int rawsock; int send_count; int recv_count; pid_t pid; struct sockaddr_in dest; struct timeval start_time; struct timeval end_time; struct timeval time_interval; /*校驗(yàn)和算法*/ unsigned short cal_chksum(unsigned short *addr, int len) { int nleft=len; int sum=0; unsigned short *w=addr; unsigned short answer=0; /*把ICMP報(bào)頭二進(jìn)制數(shù)據(jù)以2字節(jié)為單位累加起來*/ while (nleft>1) { sum+=*w++; nleft-=2; } /*若ICMP報(bào)頭為奇數(shù)個(gè)字節(jié),會(huì)剩下最后一字節(jié)。把最后一個(gè)字節(jié)視為一個(gè)2字節(jié)數(shù)據(jù)的高字節(jié),這個(gè)2字節(jié)數(shù)據(jù)的低字節(jié)為0,繼續(xù)累加*/ if ( nleft==1) { *(unsigned char *)(&answer)=*(unsigned char *)w; sum+=answer; } sum=(sum>>16)+(sum&0xffff); sum+=(sum>>16); answer=~sum; return answer; } struct timeval cal_time_offset( struct timeval begin, struct timeval end) { struct timeval ans; ans.tv_sec = end.tv_sec - begin.tv_sec; ans.tv_usec = end.tv_usec - begin.tv_usec; if (ans.tv_usec < 0) //如果接收時(shí)間的usec小于發(fā)送時(shí)間的usec,則向sec域借位 { ans.tv_sec--; ans.tv_usec+=1000000; } return ans; } void icmp_pack( struct icmp* icmphdr, int seq, int length) { int i = 0; icmphdr->icmp_type = ICMP_ECHO; icmphdr->icmp_code = 0; icmphdr->icmp_cksum = 0; icmphdr->icmp_seq = seq; icmphdr->icmp_id = pid & 0xffff; for (i=0;i<length;i++) { icmphdr->icmp_data[i] = i; } icmphdr->icmp_cksum = cal_chksum((unsigned short *)icmphdr, length); } int icmp_unpack( char * buf, int len) { int iphdr_len; struct timeval begin_time, recv_time, offset_time; int rtt; //round trip time struct ip* ip_hdr = ( struct ip *)buf; iphdr_len = ip_hdr->ip_hl*4; struct icmp* icmp = ( struct icmp*)(buf+iphdr_len); len-=iphdr_len; //icmp包長(zhǎng)度 if (len < 8) //判斷長(zhǎng)度是否為ICMP包長(zhǎng)度 { fprintf (stderr, "Invalid icmp packet.Its length is less than 8\n" ); return -1; } //判斷該包是ICMP回送回答包且該包是我們發(fā)出去的 if ((icmp->icmp_type == ICMP_ECHOREPLY) && (icmp->icmp_id == pid)) { if ((icmp->icmp_seq < 0) || (icmp->icmp_seq > PACKET_SEND_MAX_NUM)) { fprintf (stderr, "icmp packet seq is out of range!\n" ); return -1; } ping_packet[icmp->icmp_seq].flag = 0; begin_time = ping_packet[icmp->icmp_seq].begin_time; gettimeofday(&recv_time, NULL); offset_time = cal_time_offset(begin_time, recv_time); rtt = offset_time.tv_sec*1000 + offset_time.tv_usec/1000; //毫秒為單位 printf ( "%d byte from %s: icmp_seq=%u ttl=%d rtt=%d ms\n" , len, inet_ntoa(ip_hdr->ip_src), icmp->icmp_seq, ip_hdr->ip_ttl, rtt); } else { fprintf (stderr, "Invalid ICMP packet! Its id is not matched!\n" ); return -1; } return 0; } void ping_send() { char send_buf[128]; memset (send_buf, 0, sizeof (send_buf)); gettimeofday(&start_time, NULL); //記錄第一個(gè)ping包發(fā)出的時(shí)間 while (alive) { int size = 0; gettimeofday(&(ping_packet[send_count].begin_time), NULL); ping_packet[send_count].flag = 1; //將該標(biāo)記為設(shè)置為該包已發(fā)送 icmp_pack(( struct icmp*)send_buf, send_count, 64); //封裝icmp包 size = sendto(rawsock, send_buf, 64, 0, ( struct sockaddr*)&dest, sizeof (dest)); send_count++; //記錄發(fā)出ping包的數(shù)量 if (size < 0) { fprintf (stderr, "send icmp packet fail!\n" ); continue ; } sleep(1); } } void ping_recv() { struct timeval tv; tv.tv_usec = 200; //設(shè)置select函數(shù)的超時(shí)時(shí)間為200us tv.tv_sec = 0; fd_set read_fd; char recv_buf[512]; memset (recv_buf, 0 , sizeof (recv_buf)); while (alive) { int ret = 0; FD_ZERO(&read_fd); FD_SET(rawsock, &read_fd); ret = select(rawsock+1, &read_fd, NULL, NULL, &tv); switch (ret) { case -1: fprintf (stderr, "fail to select!\n" ); break ; case 0: break ; default : { int size = recv(rawsock, recv_buf, sizeof (recv_buf), 0); if (size < 0) { fprintf (stderr, "recv data fail!\n" ); continue ; } ret = icmp_unpack(recv_buf, size); //對(duì)接收的包進(jìn)行解封 if (ret == -1) //不是屬于自己的icmp包,丟棄不處理 { continue ; } recv_count++; //接收包計(jì)數(shù) } break ; } } } void icmp_sigint( int signo) { alive = 0; gettimeofday(&end_time, NULL); time_interval = cal_time_offset(start_time, end_time); } void ping_stats_show() { long time = time_interval.tv_sec*1000+time_interval.tv_usec/1000; /*注意除數(shù)不能為零,這里send_count有可能為零,所以運(yùn)行時(shí)提示錯(cuò)誤*/ printf ( "%d packets transmitted, %d recieved, %d%c packet loss, time %ldms\n" , send_count, recv_count, (send_count-recv_count)*100/send_count, '%' , time ); } int main( int argc, char * argv[]) { int size = 128*1024; //128k struct protoent* protocol = NULL; char dest_addr_str[80]; memset (dest_addr_str, 0, 80); unsigned int inaddr = 1; struct hostent* host = NULL; pthread_t send_id,recv_id; if (argc < 2) { printf ( "Invalid IP ADDRESS!\n" ); return -1; } protocol = getprotobyname( "icmp" ); //獲取協(xié)議類型ICMP if (protocol == NULL) { printf ( "Fail to getprotobyname!\n" ); return -1; } memcpy (dest_addr_str, argv[1], strlen (argv[1])+1); rawsock = socket(AF_INET,SOCK_RAW,protocol->p_proto); if (rawsock < 0) { printf ( "Fail to create socket!\n" ); return -1; } pid = getpid(); setsockopt(rawsock, SOL_SOCKET, SO_RCVBUF, &size, sizeof (size)); //增大接收緩沖區(qū)至128K bzero(&dest, sizeof (dest)); dest.sin_family = AF_INET; inaddr = inet_addr(argv[1]); if (inaddr == INADDR_NONE) //判斷用戶輸入的是否為IP地址還是域名 { //輸入的是域名地址 host = gethostbyname(argv[1]); if (host == NULL) { printf ( "Fail to gethostbyname!\n" ); return -1; } memcpy (( char *)&dest.sin_addr, host->h_addr, host->h_length); } else { memcpy (( char *)&dest.sin_addr, &inaddr, sizeof (inaddr)); //輸入的是IP地址 } inaddr = dest.sin_addr.s_addr; printf ( "PING %s, (%d.%d.%d.%d) 56(84) bytes of data.\n" ,dest_addr_str, (inaddr&0x000000ff), (inaddr&0x0000ff00)>>8, (inaddr&0x00ff0000)>>16, (inaddr&0xff000000)>>24); alive = 1; //控制ping的發(fā)送和接收 signal (SIGINT, icmp_sigint); if (pthread_create(&send_id, NULL, ( void *)ping_send, NULL)) { printf ( "Fail to create ping send thread!\n" ); return -1; } if (pthread_create(&recv_id, NULL, ( void *)ping_recv, NULL)) { printf ( "Fail to create ping recv thread!\n" ); return -1; } pthread_join(send_id, NULL); //等待send ping線程結(jié)束后進(jìn)程再結(jié)束 pthread_join(recv_id, NULL); //等待recv ping線程結(jié)束后進(jìn)程再結(jié)束 ping_stats_show(); close(rawsock); return 0; } |
編譯以及實(shí)驗(yàn)現(xiàn)象如下:
我的實(shí)驗(yàn)環(huán)境是兩臺(tái)服務(wù)器,發(fā)起ping的主機(jī)是172.0.5.183,被ping的主機(jī)是172.0.5.182,以下是我的兩次實(shí)驗(yàn)現(xiàn)象(ping IP和ping 域名)。
特別注意:
只有root用戶才能利用socket()函數(shù)生成原始套接字,要讓Linux的一般用戶能執(zhí)行以上程序,需進(jìn)行如下的特別操作:用root登陸,編譯以上程序gcc -lpthread -o ping ping.c
實(shí)驗(yàn)現(xiàn)象可以看出,PING是成功的,表明兩主機(jī)間的網(wǎng)絡(luò)是通的,發(fā)出的所有ping包都收到了回復(fù)。
下面是Linux系統(tǒng)自帶的PING程序,我們可以對(duì)比一下我們?cè)O(shè)計(jì)的PING程序跟系統(tǒng)自帶的PING程序有何不同。
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持服務(wù)器之家。