您的位置:首页 > 其它

ping

2015-12-03 00:13 267 查看
PING jd.com (211.152.123.224) 56(84) bytes of data.
64 bytes from 211.152.123.224: icmp_seq=1 ttl=52 time=28.1 ms
64 bytes from 211.152.123.224: icmp_seq=2 ttl=52 time=27.0 ms
64 bytes from 211.152.123.224: icmp_seq=3 ttl=52 time=26.5 ms
64 bytes from 211.152.123.224: icmp_seq=4 ttl=52 time=27.2 ms
64 bytes from 211.152.123.224: icmp_seq=5 ttl=52 time=29.0 ms
64 bytes from 211.152.123.224: icmp_seq=6 ttl=52 time=28.0 ms
^C
--- jd.com ping statistics ---
6 packets transmitted, 6 received, 0% packet loss, time 5248ms
rtt min/avg/max/mdev = 26.523/27.656/29.049/0.831 ms


ICMP

由上面的执行结果可以看到,ping 命令执行后显示出被测试系统主机名和相应 IP 地址、返回给当前主机的 ICMP 报文顺序号、ttl 生存时间和往返时间 rtt(单位是毫秒,即千分之一秒)。

要真正了解 ping 命令实现原理,就要了解 ping 命令所使用到的 TCP/IP 协议:ICMP 协议。

ICMP 是(Internet Control Message Protocol)Internet 控制报文协议。它是 TCP/IP 协议族的一个子协议,用于在 IP 主机、路由器之间传递控制消息。

控制消息有:目的不可达消息,超时信息,重定向消息,时间戳请求和时间戳响应消息,回显请求和回显应答消息。

ping 命令使用回显请求和回显应答消息。具体表现是向网络上的另一个主机系统发送 ICMP 报文,如果指定系统得到了报文,它将把报文一模一样地传回给发送者。

下面来看下回显请求和回显应答消息 ICMP 报文格式:



回显请求报文其中类型为 0,代码为 0。

回显应答报文其中类型为 8,代码为 0。

校验和字段:包括数据在内的整个 ICMP 协议数据包的校验和,具体实现方法会在下面详细介绍。

标识符字段:用于唯一标识 ICMP 报文,本项目使用程序的进程 id。因为如果同时在两个命令行终端执行 ping 命令的话,每个 ping 命令都会接收到所有的回显应答,所以需要根据标识符来判断回显应答是否应该接收。

序号字段:ICMP 报文的序号。

数据字段:也就是报文,本项目中我们将发送报文的时间戳放入数据字段,这样当接收到该报文应答的时候可以取出发送时间戳,将接收应答的时间戳减去发送时间戳就是报文往返时间(rtt)。提前预告一下,这里使用gettimeofday()API函数获取时间戳,详细介绍会在函数介绍部分说明。

ICMP 报文 C 语言实现可以用下面的数据结构表示:

struct icmp{
unsigned char   type;         // 类型
unsigned char   code;         // 代码
unsigned short  checksum;     // 校验和
unsigned short  id;            // 标识符
unsigned short  sequence;     // 序号
struct timeval  timestamp;    // 时间戳
};


二、地址信息表示

当我们编写网络应用程序时,必然要使用地址信息指定数据传输给网络上哪个主机,那么地址信息应该包含哪些呢?

1.地址族,基于IPv4的地址族还是IPv6的地址族。

2.IP地址。

3.端口号。

为了便于记录地址信息,系统定义了如下结构体:

struct sockaddr_in{
sa_family_t        sin_family;     // 地址族
uint16_t        sin_port;        // 端口号
struct in_addr  sin_addr;          // 32位IP地址
char             sin_zero[8];    // 不使用
};


其中struct in_addr结构体定义如下:

struct in_addr{
in_addr_t     s_addr;        // 32位IP地址
};


in_addr_t使用如下宏指令定义,也就是无符号整型32位。

#define in_addr_t uint32_t


三、主机字节序与网络字节序

在不同CPU中,4字节整型数值1在内存空间的保存方式是不同的。

4字节整型数值1可用二进制表示如下:

00000000 00000000 00000000 00000001

而有些CPU则以倒序保存。

00000001 00000000 00000000 00000000

所以,如果发送主机与接收主机CPU字节序不一样则就会出现问题。

引申上面的问题,这就涉及到CPU解析数据的方式,其方式有2种:

大端序(Big Endian):高位字节存放到低位地址。

小端序(Little Endian):高位字节存放到高位地址。由于不同CPU字节序不一样,因此,在通过网络传输数据时约定统一方式,这种约定称为网络字节序(Network Byte Order),非常简单——统一为大端序。

所以,进行网络传输数据前,需要先把数据数组转化为大端序格式再进行网络传输。接收到网络数据后,需要转换本机字节序格式然后进行后续处理。不过,幸运地是这些工作不需要我们自己完成,系统会自动转换的。

我们唯一需要转换的是向struct sockaddr_in结构体变量填充IP地址和端口号数据的时候。当然,系统已经提供了一些函数,只需调用相应函数即可。

转换字节序的函数有:

unsigned short htons(unsigned short);
unsigned short ntohs(unsigned short);
unsigned long  htonl(unsigned long);
unsigned long  ntohl(unsigned long);


上面的函数非常简单,通过函数名就能知道它们的功能,htonl/htons中的h代表主机(host)字节序,n代表网络(network)字节序,s指的是short,l指的是long(需要注意一下,linux中long类型只占4个字节,跟int类型一样)。

下面通过简单的示例熟悉一下上面的函数,代码如下所示

#include <stdio.h>
#include <arpa/inet.h>

int main(void){
unsigned short hosts = 0x1234;
unsigned short nets;
unsigned long  hostl = 0x12345678;
unsigned long  netl;

nets = htons(hosts);
netl = htonl(hostl);

printf("Host ordered short: %#x \n", hosts);
printf("Network ordered short: %#x \n", nets);

printf("Host ordered long: %#lx \n", hostl);
printf("Network ordered long: %#lx \n", netl);

return 0;
}


将上面代码写入order.c文件中,然后编译执行,结果如下。

sudo gcc htons.c -o hton

从上面执行结果可以看出,主机字节序与网络字节序不一样,是小端序的。大家通过上面的程序也可以判断自己主机是大端序的还是小端序的。多说一句,Intel系列CPU采用的都是小端序标准。

四、函数介绍

首先介绍第一个函数gettimeofday()。

#include <sys/time.h>
int gettimeofday(struct timeval *tv, struct timezone *tz);


该函数的作用是把当前的时间放入struct timeval结构体中返回。

注意:

1.精确级别,微妙级别

2.受系统时间修改影响

3.返回的秒数是从1970年1月1日0时0分0秒开始

其参数tv是保存获取时间结果的结构体,参数tz用于保存时区结果。

结构体timeval的定义为:

struct timeval
{
long int tv_sec;      // 秒数
long int tv_usec;     // 微秒数
}


结构体timezone的定义为:

struct timezone
{
int tz_minuteswest;/*格林威治时间往西方的时差*/
int tz_dsttime;    /*DST 时间的修正方式*/
}


timezone 参数若不使用则传入0即可,本项目传入0。

下面通过计算程序运行时间的一个简单示例感受一下gettimeofday函数的使用,大家可以先动手自己实现一下,然后再看看下面的实现
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: