一、实现原理

ping利用ICMP协议包来侦测另一个主机是否可达。Ping的原理是使用了类型码为8的ICMP回送请求包,收到请求的主机则用类型码为0的ICMP回应报文。如果应答包和请求包的标示号、序号和内容相同的话,则证明能够ping通,网络设备可用。ping程序计算间隔时间,并计算有多少个包被送达,用户就可以判断网络大致的情况。

系统提供的Ping还列出来了传送的时间和TTL等数据,命令提供了多种参数。下面的程序用ICMP协议实现了简单的Ping功能。由于用的是ICMP协议,因此,我们不能够通过建立一个SOCK_STREAM或SOCK_DGRAM来发送这个包,只能够使用SOCK_RAW来自己构造数据包。

二、ping程序的工作流程

三、主要函数和数据结构

//定义IP头部

typedef struct iphdr {

unsigned int h_len:4; // 头部长

unsigned int version:4; // 版本号

unsigned char tos; // 服务类型

unsigned short total_len; // 总长度

unsigned short ident; // 标识

unsigned short frag_and_flags; //标志

unsigned char ttl; //生存时间

unsigned char proto; // 上层协议

unsigned short checksum; // 校验和

unsigned int sourceIP; //源IP

unsigned int destIP; //目的IP

unsigned long

RoundTripTime; // RTT in milliseconds

}IpHeader;

// 定义ICMP 头部

typedef struct icmphdr {

BYTE i_type; //类型

BYTE i_code; //代码

USHORT i_cksum; //校验和

USHORT i_id; //标识

USHORT i_seq; //序列号

ULONG timestamp; //数据

}IcmpHeader;

#define STATUS_FAILED 0xFFFF

#define DEF_PACKET_SIZE

32

//默认数据包长度

#define DEF_PACKET_NUMBER 4

//默认发送ICMP请求的次数

#define MAX_PACKET 1024

//数据包最大长度

#define xmalloc(s)

HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,(s))

//分配内存

#define xfree(p) HeapFree (GetProcessHeap(),0,(p))

//释放内存

//填充icmp数据包

void fill_icmp_data(char *, int);

//计算校验和

USHORT checksum(USHORT *, int);

//收到数据后解码

int decode_resp(char *,int ,struct sockaddr_in

*);

//计算接收耗费的时间的最小,最大和平均值

void PX(int *input, int inputlen, int

*output);

四、ping程序的具体实现

#define ICMP_ECHO 8 //ICMP回显请求

#define ICMP_ECHOREPLY 0 //ICMP回显应答

#define ICMP_MIN 8 //ICMP数据包最短为8个字节

//定义IP头部

typedef struct iphdr {

unsigned int h_len:4; // 头部长

unsigned int version:4; // 版本号

unsigned char tos; // 服务类型

unsigned short total_len; // 总长度

unsigned short ident; // 标识

unsigned short frag_and_flags; //标志

unsigned char ttl; //生存时间

unsigned char proto; // 上层协议

unsigned short checksum; // 校验和

unsigned int sourceIP; //源IP

unsigned int destIP; //目的IP

unsigned long

RoundTripTime; // RTT in milliseconds

}IpHeader;

// 定义ICMP 头部

typedef struct icmphdr {

BYTE i_type; //类型

BYTE i_code; //代码

USHORT i_cksum; //校验和

USHORT i_id; //标识

USHORT i_seq; //序列号

ULONG timestamp; //数据

}IcmpHeader;

#define STATUS_FAILED 0xFFFF

#define DEF_PACKET_SIZE

32

//默认数据包长度

#define DEF_PACKET_NUMBER 4

//默认发送ICMP请求的次数

#define MAX_PACKET 1024

//数据包最大长度

#define xmalloc(s)

HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,(s))

//分配内存

#define xfree(p) HeapFree (GetProcessHeap(),0,(p))

//释放内存

//填充icmp数据包

void fill_icmp_data(char *, int);

//计算校验和

USHORT checksum(USHORT *, int);

//收到数据后解码

int decode_resp(char *,int ,struct sockaddr_in

*);

//计算接收耗费的时间的最小,最大和平均值

void PX(int *input, int inputlen, int

*output);

//记录时间的变量

int temptime=0;

//提示用户该程序使用方法

void Usage(char *progname){

fprintf(stderr,"Usage:\n");

fprintf(stderr,"%s [number of packets]

[data_size]\n",progname);

fprintf(stderr,"datasize can be up to 1Kb\n");

ExitProcess(STATUS_FAILED);

}

int main(int argc, char **argv)

{

WSADATA wsaData; //初始化windows socket需要的参数

SOCKET sockRaw; //原始套接字

struct sockaddr_in dest,from; //源、目的IP地址

struct hostent * hp; //指针指向包含主机名、地址列表等信息的结构体

int bread,datasize,times;

int fromlen = sizeof(from);

int timeout = 1000; //超时时间1000ms

int statistic = 0; // 用于统计

char *dest_ip;

char *icmp_data;

char *recvbuf;

unsigned int addr=0;

USHORT seq_no = 0;

int num=4;

if(argc>2)

{

num=atoi(argv[2]);

if(num == 0)

num=DEF_PACKET_NUMBER;

}

int

*TIME=(int*)malloc(num*sizeof(int));

for(int

k=0;k

{

TIME[k]=0;

}

int

ST[3]={0};

if (WSAStartup(MAKEWORD(2,1),&wsaData) !=

0){

fprintf(stderr,"WSAStartup failed:

%d\n",GetLastError());

ExitProcess(STATUS_FAILED);

}

//使用方法不对时显示提示信息

if (argc <2 ) {

Usage(argv[0]);

}

//创建原始套接字

sockRaw = WSASocket(AF_INET,SOCK_RAW,IPPROTO_ICMP,NULL,

0,WSA_FLAG_OVERLAPPED);

//注:为了使用发送接收超时设置(即设置SO_RCVTIMEO, SO_SNDTIMEO),

//

必须将标志位设为WSA_FLAG_OVERLAPPED !

// 创建原始套接字不成功

if (sockRaw == INVALID_SOCKET) {

fprintf(stderr,"WSASocket() failed:

%d\n",WSAGetLastError());

ExitProcess(STATUS_FAILED);

}

//设定发送超时时间

timeout = 1000;

bread =

setsockopt(sockRaw,SOL_SOCKET,SO_SNDTIMEO,(char*)&timeout,

sizeof(timeout));

if(bread == SOCKET_ERROR) {

fprintf(stderr,"failed to set send timeout:

%d\n",WSAGetLastError());

ExitProcess(STATUS_FAILED);

}

//设定接收数据超时时间

bread =

setsockopt(sockRaw,SOL_SOCKET,SO_RCVTIMEO,(char*)&timeout,

sizeof(timeout));

if(bread == SOCKET_ERROR) {

fprintf(stderr,"failed to set recv timeout:

%d\n",WSAGetLastError());

ExitProcess(STATUS_FAILED);

}

memset(&dest,0,sizeof(dest));

//解析用户输入的目标地址

hp = gethostbyname(argv[1]);

if (!hp){

addr = inet_addr(argv[1]);

}

//非法输入

if ((!hp) && (addr == INADDR_NONE)

) {

fprintf(stderr,"Unable to resolve %s\n",argv[1]);

ExitProcess(STATUS_FAILED);

}

//记录目标主机信息的结构体

//地址

if (hp != NULL)

memcpy(&(dest.sin_addr),hp->h_addr,hp->h_length);

else

dest.sin_addr.s_addr = addr;

//协议族

if (hp)

dest.sin_family = hp->h_addrtype;

else

dest.sin_family = AF_INET;

//目标IP

dest_ip = inet_ntoa(dest.sin_addr);

//除了目标地址,还给出了Ping的次数

if(argc>2)

{

times=atoi(argv[2]);

if(times == 0)

times=DEF_PACKET_NUMBER;

}

else

times=DEF_PACKET_NUMBER;

//还给出了数据大小

if (argc >3)

{

datasize = atoi(argv[3]);

//给的是0,则用默认数据包大小

if (datasize == 0)

datasize = DEF_PACKET_SIZE;

//用户给出的数据包大小太大

if (datasize >1024)

{

fprintf(stderr,"WARNING : data_size is too large !\n");

datasize = DEF_PACKET_SIZE;

}

}

else

datasize = DEF_PACKET_SIZE;

datasize += sizeof(IcmpHeader);

icmp_data = (char*)xmalloc(MAX_PACKET);

recvbuf = (char*)xmalloc(MAX_PACKET);

if (!icmp_data) {

fprintf(stderr,"HeapAlloc failed %d\n",GetLastError());

ExitProcess(STATUS_FAILED);

}

memset(icmp_data,0,MAX_PACKET);

//填充ICMP数据包,类型、代码、标识等

fill_icmp_data(icmp_data,datasize);

//提示正在ping目标主机

fprintf(stdout,"\nPinging %s ....\n\n",dest_ip);

//Ping多次

for(int i=0;i

{

int bwrote;

//准备ICMP包头部数据

((IcmpHeader*)icmp_data)->i_cksum = 0;

//取得以毫秒为单位的计算机启动后经历的时间间隔

((IcmpHeader*)icmp_data)->timestamp =

GetTickCount();

((IcmpHeader*)icmp_data)->i_seq = seq_no++;

//序列号递增

((IcmpHeader*)icmp_data)->i_cksum =

checksum((USHORT*)icmp_data,datasize);//计算校验和

//发送ICMP数据包

bwrote = sendto(sockRaw,icmp_data,datasize,0,(struct

sockaddr*)&dest,sizeof(dest));

//发送失败

if (bwrote == SOCKET_ERROR){

if (WSAGetLastError() == WSAETIMEDOUT) {

printf("Request timed out.\n");

continue;

}

fprintf(stderr,"sendto failed: %d\n",WSAGetLastError());

ExitProcess(STATUS_FAILED);

}

if (bwrote < datasize ) {

fprintf(stdout,"Wrote %d bytes\n",bwrote);

}

//接收应答数据

bread = recvfrom(sockRaw,recvbuf,MAX_PACKET,0,(struct

sockaddr*)&from,&fromlen);

//接收失败

if (bread == SOCKET_ERROR){

if (WSAGetLastError() == WSAETIMEDOUT) {

printf("Request timed out.\n");

continue;

}

fprintf(stderr,"recvfrom failed:

%d\n",WSAGetLastError());

ExitProcess(STATUS_FAILED);

}

//成功解码

if(!decode_resp(recvbuf,bread,&from))

statistic++; //记录成功接收响应数据包的次数

TIME[i] = temptime;

Sleep(1000);

}

PX(TIME, times,

ST);

//统计运行Ping命令的统计结果

fprintf(stdout,"\nPing statistics for %s \n",dest_ip);

fprintf(stdout,"

Packets: Sent = %d,Received = %d, Lost = %d (%2.0f%%

loss)\n",times,

statistic,(times-statistic),(float)(times-statistic)/times*100);

printf("Approximate

round trip times in milli-seconds:\n" );

printf("

Minimum = %dms, Maximum = %dms, Average =

%dms\n",ST[1],ST[0],ST[2]);

// 关闭,回收资源

WSACleanup();

return 0;

}

//收到响应IP数据包后,对其进行解码

int decode_resp(char *buf, int bytes,struct

sockaddr_in *from)

{

IpHeader *iphdr;

IcmpHeader *icmphdr;

unsigned short iphdrlen;

iphdr = (IpHeader *)buf;

iphdrlen = (iphdr->h_len) * 4 ;

//头部占几个节字节

// 报文太短

if (bytes < iphdrlen + ICMP_MIN) {

printf("Too few bytes from

%s\n",inet_ntoa(from->sin_addr));

}

//找到ICMP数据包开始的地方

icmphdr = (IcmpHeader*)(buf + iphdrlen);

// 是否是回复报文类型

if (icmphdr->i_type != ICMP_ECHOREPLY) {

fprintf(stderr,"non-echo type %d

recvd\n",icmphdr->i_type);

return 1;

}

//是不是发给本程序的数据包

if (icmphdr->i_id != (USHORT)GetCurrentProcessId())

{

fprintf(stderr,"someone else's packet!\n");

return 1;

}

temptime=GetTickCount()-icmphdr->timestamp;

printf("Reply from

%s:", inet_ntoa(from->sin_addr));

printf(" bytes=%d ", bytes);

printf(" time= %d ms ", temptime); //发送到接收过程的经历的时间

printf(" TTL=%d

",iphdr->ttl);

printf(" icmp_seq = %d

",icmphdr->i_seq);

printf("\n");

return 0;

}

//计算校验和

USHORT checksum(USHORT *buffer, int size)

{

unsigned long cksum=0;

while(size >1) {

cksum+=*buffer++;

size -=sizeof(USHORT);

}

if(size) {

cksum += *(UCHAR*)buffer;

}

cksum = (cksum >> 16) + (cksum

& 0xffff);

cksum += (cksum >>16);

return (USHORT)(~cksum);

}

//填充ICMP数据包

void fill_icmp_data(char * icmp_data, int

datasize){

IcmpHeader *icmp_hdr;

char *datapart;

icmp_hdr = (IcmpHeader*)icmp_data;

icmp_hdr->i_type =

ICMP_ECHO;

icmp_hdr->i_code = 0;

icmp_hdr->i_id =

(USHORT)GetCurrentProcessId();

icmp_hdr->i_cksum = 0;

icmp_hdr->i_seq = 0;

datapart = icmp_data + sizeof(IcmpHeader);

//数据区随便填充

memset(datapart,17, datasize - sizeof(IcmpHeader));

}

void PX(int *input, int inputlen, int

*output)

{

int I;

int

min=input[0];

int max=input[0];

int

Num=input[0];

for(I=1;

I

{

if(min

> input[I])

{

min

= input[I];

}

if(max

< input[I])

{

max

= input[I];

}

Num = Num +

input[I];

}

output[0] =

max;

output[1] = min;

output[2] =

(Num/inputlen);

}

五、ping程序运行结果

带有发送次数参数的操作:

c语言实现icmp协议ping命令,利用ICMP协议实现ping命令相关推荐

  1. 使用smtp协议的服务器是,SMTP协议使用

    好文网为大家准备了关于SMTP协议使用的文章,好文网里面收集了五十多篇关于好SMTP协议使用好文,希望可以帮助大家.更多关于SMTP协议使用内容请关注好文网.ctrl+D请收藏!篇一:利用SMTP协议 ...

  2. 利用WireShark分析由Ping产生的Internet 控制报文协议(ICMP)

    2019独角兽企业重金招聘Python工程师标准>>> ICMP是(Internet Control Message Protocol)Internet控制报文协议.它是TCP/IP ...

  3. ARP协议、路由、ICMP协议(ping命令)、三层交换机

    本机ip:192.168.0.237/24                      ping  192.168.90.1 ping的过程: 1.如果是域名,先进行域名解析到具体的一个ip 2.拿目的 ...

  4. ICMP 隧道——将流量封装进 IMCP 的 ping 数据包中,旨在利用 ping 穿透防火墙的检测...

    利用 ICMP 隧道穿透防火墙 转自:http://xiaix.me/li-yong-icmp-sui-dao-chuan-tou-fang-huo-qiang/ 以前穿透防火墙总是使用 SSH 隧道 ...

  5. ping 原理与ICMP协议---转

    http://blog.csdn.net/inject2006/article/details/2139149 ping 的原理 ping 程序是用来探测主机到主机之间是否可通信,如果不能ping到某 ...

  6. 【计算机网络】(5)ping的过程分析+icmp协议

    目录 一.ping 1.1 过程分析 1.2 ICMP协议 1.2.1 iptables简要介绍 1.2.2 路由追踪 1.3 常用选项 1.4 ping一台服务器ping不通的原因和解决方法 1.5 ...

  7. 利用ICMP协议,使用python原始套接字实现主机存活探测工具

    一.课题概述. 一学期一次的课程设计终于开始了(停课两周,马上放寒假了,哈哈哈哈哈哈...)这次我们课程设计的科目是计算机协议,我们小组抽到的题目是利用ICMP模仿ping命令写一个主机存活探测的工具 ...

  8. ping命令使用什么协议

    几乎每一名网工及弱电工程师都遇到过网络不通的情况,通常判断标准是以外网连通性及 Ping 测试得出结果,而 ping 属于 ICMP 协议中的一个工具.ping 是为了测试另一台主机是否可达,发送一份 ...

  9. TCP/IP协议学习( 三 ) ---- ping原理 和 ICMP

    TCP/IP协议学习( 三 ) ---- ping原理 和 ICMP 1. 命令ping的用法和解析 1.1 ping 的用法 1.2 TTL 是什么? 1.3 SEQ是什么? 2.ICMP协议 2. ...

最新文章

  1. 企业架构:现代数据架构的特征
  2. 音视频编解码: YUV采样格式中的YUV444,YUV422,YUV420理解
  3. SAP S/4HANA生产订单创建时使用的工厂数据是从什么地方带出来的 1
  4. Erlang TCP Socket的接收进程的2种方案
  5. java 单例 读写锁_终极锁实战:单JVM锁+分布式锁
  6. 检验int值在list中是否存在_R语言统计与绘图:卡方检验
  7. mysql大批量数据写入_存储过程写入大批量数据,用于测试mysql查询优化
  8. 工作中遇到的错误记录
  9. Windos环境用Nginx配置反向代理和负载均衡
  10. 史上最大漏洞危机再生新变种,大量芯片受感染
  11. MySQL 03-MySQL安装-直接解压二进制文件(CentOS6)
  12. python excel案例导入jira_用Python脚本批量添加JIRA用户,python,jira
  13. Centos挂载iscsi存储
  14. Java 通过JDBC连接Mysql数据库
  15. SpringBoot中Session超时原理说明
  16. mac安装jdk1.8
  17. Hard samples mining
  18. jQuery学习(菜鸟教程)
  19. 第六次作业——潘芊睿
  20. mysql完美国际数据库_完美国际 数据库

热门文章

  1. Flash 不缓存XML 数据 用时间戳getTime(非getTimer)抗拒缓存
  2. SharePoint Server 2013新特性之Yammer
  3. centos7.6下oracle12cR2 静默安装
  4. 程序员的自我修养--编译链接资料收集
  5. silk 编解码_Silk解码 开发日志
  6. linux 命令:nc、netcat、ncat、socat
  7. Python选择流程(二)
  8. 3D打印S3d参数设置
  9. 2012秋江苏省计算机二级上机试题,2011秋-2012秋江苏计算机等级考试上机
  10. 安卓刷机时代不再!魔趣开源项目创始人宣布:“决定删库跑路了”