-

Ping.c
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
//
这里告诉我们
linux
下的
libc5
库是不提供
icmp
支持的,
所以如果是
libc5
库的
系统上如果要
ping ,
即需要自己定义相应的
icmp
结构
/* It turns out that libc5 doesn't have
proper icmp support
* built into it
header files, so we have to supplement it */
#if __GNU_LIBRARY__ < 5
static
const
int
ICMP_MINLEN
=
8;
//icmp
最小也要有
8
位,用来校验是判
断
/* abs minimum */
//
< br>以下是定义
icmp
结构,通常
icmp
是通过
ip
包发送的,所以发
送的数据在
通常是
在外先裹层
icmp
报头,然后在进入网络层裹上
ip<
/p>
报头一起发送的,所以接受时,我们往往
要跳过
< br>ip
报头
然后把
icmp
的东西取出来判断
,这里对
ip
报头不分析,仅仅定义
libc5
需要的
icmp
结构
//
常见的
icmp
通常由
type
code
cksum
组成报头,
然后是根据不同需求需要发送的
data
字段,其
中不同需求
由
type
的代码的不同
而对
dat
a
字段的包含也不同,而
ping
中通
常用的是
ICMP_ECHO
;co
de
是代码段,
ping
中通常为
0
;cksum
是网络中
ip
等通常用的校
验算法,算法下面解释。
struct icmp_ra_addr
{
u_int32_t ira_addr;
u_int32_t ira_preference;
};
struct icmp
{
u_int8_t icmp_type;
/* type of message, see below
*/
//
这是
type
u_int8_t icmp_code;
/* type sub
code */
//
这是
code
u_int16_t icmp_cksum;
/* ones
complement checksum of struct */
//
校验
//
注意下面用了
union
结构,这
是因为
icmp
除了探测主机,即
pi
ng
外还有其他的功能,根
据功能码的不同每次发出去的下面的
数据段结构其实是不一样的,在这里我们只需用到
struct
ih_idseq
因为这是
ping
所需要的结构,其他的
icmp
管理,
我们暂时不用管,因为一
次只会用到一种数据类型
,
所以用
union
定义一个最大的空间
union
//
发
送的各个
data
的最大值
{
u_char ih_pptr;
/*
ICMP_PARAMPROB */
struct
in_addr ih_gwaddr;
/* gateway address */
struct
ih_idseq
/* echo datagram */ //echo
是我
们关心程序发送给主机用来探
测的
{
u_int16_t icd_id;
//
这个是
id
u_int16_t
icd_seq;
//
这个是
seq
就是包的顺序
} ih_idseq;
u_int32_t ih_void;
/* ICMP_UNREACH_NEEDFRAG -- Path MTU
Discovery (RFC1191) */
struct ih_pmtu
//<
/p>
这个是关于
MTU
路径的。不用
{
u_int16_t
ipm_void;
u_int16_t ipm_nextmtu;
} ih_pmtu;
struct
ih_rtradv
{
u_int8_t irt_num_addrs;
u_int8_t irt_wpa;
u_int16_t
irt_lifetime;
} ih_rtradv;
} icmp_hun;
#define
icmp_pptr
icmp__pptr
//
这个就不多说了,
为了使用
方便的定义
#define
icmp_gwaddr
icmp__gwaddr
#define
icmp_id
icmp___id
#define
icmp_seq
icmp___seq
#define
icmp_void
icmp__void
#define
icmp_pmvoid
icmp___void
#define
icmp_nextmtu
icmp___nextmtu
#define
icmp_num_addrs
icmp___num_addrs
#define
icmp_wpa
icmp___wpa
#define
icmp_lifetime
icmp___lifetime
union
//<
/p>
这里定义必要的主机回复的
Union
结
构,
Ping
中需要用的是
id_ts
;
代表响应时间
{
struct
{
u_int32_t its_otime;
u_int32_t
its_rtime;
u_int32_t its_ttime;
} id_ts;
struct
{
struct ip idi_ip;
/* options and then 64
bits of data */
} id_ip;
struct icmp_ra_addr id_radv;
u_int32_t
id_mask;
u_int8_t
id_data[1];
} icmp_dun;
#define
icmp_otime
icmp___otime
//
这个同上
#define
icmp_rtime
icmp___rtime
#define
icmp_ttime
icmp___ttime
#define
icmp_ip
icmp___ip
#define
icmp_radv
icmp__radv
#define
icmp_mask
icmp__mask
#define
icmp_data
icmp__data
};
#endif
//
这里
icmp
结构定义结束,好象很复杂,但是在
< br>ping
中真正用到的不多,还有一点,在
IP
报头中我们还需要用到
IP
报头长度
IHL
和生存时间
TTL
,
报头长度用来让我们在
ping
时如
果通过
IP
路由转发后连
IP
报头一起返回时跳过到
icmp
而
ttl
是我们
ping
中需要的,而还
需说明的是
ip
因为是点对点的,不是
tcp,
所以
ping
中不需要
bind
和
listen
只需把信息发送
出去即可
;
切记
//
下面是数据包大小定义
,
icmp
中最小也要有
8
位,否则就不是
icmp
因为<
/p>
icmp
报头包含
type
、
code
和
cksum<
/p>
和这三项内容,长度为
8
位,
8
位和
16
位
,
然后
id
是
16
位的,
seq
是
16
位的,合起来正好
8
< br>个字节,这也是
icmp
的最小长度了。而最大长度则看
下面的定义
//
公用基本的数据定义
static const int DEFDATALEN =
56;
//
默认报头长度
static const int MAXIPLEN =
60;
//
一个
最大
ip
报头长度
static const int MAXICMPLEN =
76;
//
一个最大
icmp
报头长度
static const int MAXPACKET =
65468;
< br>//
最大
packet
,
icmp
规定最大发送数据
data=64k
#define
MAX_DUP_CHK
(8 * 128) //<
/p>
这个
Ping
中没用到,其他地方有用<
/p>
,
128
个字
节
static const int MAXWAIT =
10;
//
最大等
10
秒
static
const int PINGINTERV
AL = 1;
//
两次间隔一秒
/* second */
#define O_QUIET
(1 << 0)
//
退出
//
这段是其他管理用到的,
pin
g
程序中没起到什么作用
#define
A(bit)
rcvd_tbl[(bit)>>3]
/* identify
byte in array */
#define
B(bit)
(1 << ((bit) & 0x07))
/* identify
bit in byte */
#define
SET(bit)
(A(bit) |= B(bit))
#define
CLR(bit)
(A(bit) &= (~B(bit)))
#define
TST(bit)
(A(bit) &
B(bit))
static void
ping(const char *host);
/*
common routines */
static int
in_cksum(unsigned short *buf, int sz)
//
计算校验和的函数
{
int nleft = sz;
int sum = 0;
unsigned
short *w = buf;
unsigned
short ans = 0;
while (nleft > 1) {
sum += *w++;
//
这个意思是把前面的长度以
2
字节位单位加起来,
形成
和
sum,
为什么以
2
字
节为单位呢,因为
cksum
是个
2<
/p>
字节大小的校验和
nleft -= 2;
}
if (nleft == 1) {
*(unsigned char *) (&ans) = *(unsigned
char *) w;
//
如果为奇数时,会多出一个,
把它作为
两字节数据的高
16-8
位,它的
8-0
位添零后在和原
sum
相加。这样保证所有长度都加上了
sum += ans;
}
sum = (sum >> 16) + (sum & 0xFFFF);
//
然后是取反求得校验和的过程,这是网络上
常用的校验过程
sum += (sum
>> 16);
ans = ~sum;
return (ans);
}
/* simple version */
#ifndef
BB_FEATURE_FANCY_PING
//
分两种情况
,这种没参数
static
char *hostname = NULL;
static
void
noresp(int
ign)
//
无返
回包输出无值信
息
{
printf(
exit(0);
}
static void
ping(const char *host)
//
简单版下的
ping
程序
{
struct hostent *h;
struct sockaddr_in pingaddr;
struct icmp *pkt;
int pingsock, c;
char packet[DEFDATALEN + MAXIPLEN +
MAXICMPLEN];
//
申请
空间大小,一个
ping
包一般最大也只能有那么大了
pingsock =
create_icmp_socket();
//
建立
icmp
连接
memset(&pingaddr,
0,
sizeof(struct
sockaddr_in));
//
初
始
化
p
ingaddr
地
址
大
小
sockaddr_in
的内存空间
_family =
AF_INET;
h = xgethostbyname(host);
//
得到主机名
memcpy(&_addr, h->h_addr,
sizeof(_addr)); //
同理
hostname = h->h_name;
pkt = (struct icmp *) packet;
//
< br>创建
icmp
包
memset(pkt, 0, sizeof(packet));
pkt->icmp_type = ICMP_ECHO;
//
告之所发送的是探测主机类型的
icmp
即
ping
pkt->icmp_cksum = in_cksum((unsigned
short *) pkt, sizeof(packet));
//
进行校验和计算后添
入发送包
c = sendto(pingsock, packet,
sizeof(packet), 0,
//
发送给想探测的机器
(struct sockaddr *)
&pingaddr, sizeof(struct sockaddr_in));
if (c < 0 || c !=
sizeof(packet))
//
可能是网络故障,网线不通?
perror_msg_and_die(
signal(SIGALRM,
noresp);
//
如果超时无响应则退出到
noresp,
告之机器无响
应
alarm(5);
//
等5秒
< br>,
简单版就一次
ping
,
要不成功,
要不失败
/*
give the host 5000ms to
respond */
/* listen for replies */
while (1) {
struct sockaddr_in
from;
size_t fromlen = sizeof(from);
//
接受机器的返回
if ((c = recvfrom(pingsock, packet,
sizeof(packet), 0,
(struct
sockaddr *) &from, &fromlen)) < 0) {
if (errno == EINTR)
continue;
perror_msg(
continue;
}
//icmp
的最大长度为76
,
如果
大于,主机返回了
ip
报头,有这种情况
//
如果返回
ip
报头,首先我们要跳过
ip
报头,然后才能进行
icmp
的判断,这就需要
ihl
长
度了
if (c >= 76) {
/* ip + icmp
*/
struct iphdr *iphdr =
(struct iphdr *) packet;
pkt
=
(struct
icmp
*)
(packet
+
(iphdr->ihl
<<
2));
/*
skip
ip
hdr
*/
//
跳过
ip
报头
ihl<<2
是因为
ihl
是以
4
字节为一个单位来记录
IP<
/p>
报
头的长度
,
packet+()
则跳过长度
if (pkt->icmp_type ==
ICMP_ECHOREPL
Y) //
如果回复是
ping
应答类型
则成功
break;
}
}
printf(
//
告诉收到主机返回
return;
}
extern int ping_main(int argc, char
**argv)
//
简单的
ping main
甚至不带除主机名之外任
何参数
{
argc--;
argv++;
if (argc < 1)
show_usage();
ping(*argv);
//
执行
ping
return EXIT_SUCCESS;
}
//
以上是一个简单的
ping
程序,很简单,只是探测和返回通不通,即没发送什么数据,连时<
/p>
-
-
-
-
-
-
-
-
-
上一篇:和谐作文之英语作文在和谐社会中
下一篇:新编跨文化交际英语教程(许力生)课后翻译