1.概述
PING协议是用来检验本地主机与远程主机是否连接,发送的是ICMP ECHO_REQUEST包。普通的套接字是基于TCP或者是UDP的,无法发送ICMP包,所以必须用原始套接字来实现。PING协议的客户端类型值为8,代码值为0,表示请求。而PING协议的响应端类型值为0,代码值也为0,表示应答. 以太网数据部分的最小值为46字节,而IP首部占20个字节,ICMP的首部占8个字节,所以PING的数据部分至少为4字节。
2.实现细节
主机端:
(1)创建原始套接字 socket(AF_INET,SOCK_RAW,htons(proto)),能够直接得到IP包
(2)填写ICMP首部和数据部分,即icmp_type(8),icmp_code(0)和icmp_data部分
(3)封装后发送ICMP请求包
响应端:
(1)创建原始套接字socket(AF_INET,SOCK_RAW,htons(proto))
(2)填写ICMP首部和数据部分,即icmp_type(0),icmp_code(0),icmp_data
(3)发送ICMP响应包
主机端收到ICMP响应包之后,即原始的IP包,将收到包的时间减去包的发送时间就可以得到响应时延。
3. PING协议的实现例子
#include <sys/socket.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>
#include <stdlib.h>
#include <stdio.h>
/**
通过原始套接字发送ICMP回显请求报文来实现ping协议
ICMP回显报文的结构
struct icmp{
u_int8_t icmp_type;//消息类型
u_int8_t icmp_code;//消息类型的子码
u_int16_t icmp_cksum;//校验和
union{
struct ih_idseq{//显示数据报
u_int16_t icd_id;//数据报所在进程的ID
u_int16_6 icd_seq;//数据报序号
}ih_idseq;
}icmp_hun;
#define icmp_id icmp_hun.ih_idseq.icd_id
#define icmp_seq icmp_hun.ih_idseq.icd_seq
union{
u_int8_t id_data[i];//数据
}icmp_dun;
#define icmp_data icmp_dun.id_data
}
**/
typedef struct pingm_packet{
struct timeval tv_begin;//发送的时间
struct timeval tv_end;//接收到响应包的时间
short seq;//序号值
int flag;//1表示已经发送但没有接收到回应包,0表示接收到响应包
}pingm_packet;
//保存已经发送包的状态值
static pingm_packet pingpacket[128];//定义一个包数组
static pingm_packet *icmp_findpacket(int seq);
static unsigned short icmp_cksum(unsigned char*data,int len);
static struct timeval icmp_tvsub(struct timeval end,struct timeval begin);
static void icmp_statistics(void);
static void icmp_pack(struct icmp*icmph,int seq,struct timeval *tv,int length);
static int icmp_unpack(char*buf,int len);
static void* icmp_recv(void*argv);
static void* icmp_send(void*argv);
static void icmp_sigint(int signo);
static void icmp_usage();
#define K 1024
#define BUFFERSIZE 512
static unsigned char send_buff[BUFFERSIZE];//定义发送缓冲区的大小
static unsigned char recv_buff[2*K];//定义接收缓冲区的大小,为防止接收端溢出,接收缓冲区稍微大一些
static struct sockaddr_in de