博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
扯谈网络编程之自己实现ping
阅读量:6647 次
发布时间:2019-06-25

本文共 5522 字,大约阅读时间需要 18 分钟。

ping是基于ICMP(Internet Control Message Protocol)协议实现的。而ICMP协议是在IP层实现的。

ping实际上是发起者发送一个Echo Request(type = 8)的,远程主机回应一个Echo Reply(type = 0)的过程。

为什么用ping不能測试某一个port

刚開始接触网络的时候,可能非常多人都有疑问,怎么用ping来測试远程主机的某个特定port?

事实上假设看下ICMP协议,就能够发现ICMP里根本没有port这个概念,也就根本无法实现測试某一个port了。

ICMP协议的包格式(来自wiki):

  Bits 0–7 Bits 8–15 Bits 16–23 Bits 24–31
(20 bytes)
Version/IHL Type of service Length
Identification flags and offset
Time To Live (TTL) Protocol Checksum
Source IP address
Destination IP address
ICMP Header
(8 bytes)
Type of message Code Checksum
Header Data
ICMP Payload
(optional)
Payload Data
Echo Request的ICMP包格式(from wiki):

00 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
Type = 8 Code = 0 Header Checksum
Identifier Sequence Number
Data

Ping怎样计算请问耗时

在ping命令的输出上,能够看到有显示请求的耗时。那么这个耗时是怎么得到的呢?

64 bytes from 192.168.1.1: icmp_seq=1 ttl=64 time=6.28 ms

从Echo Request的格式里,看到不时间相关的东东,可是由于是Echo,即远程主机会原样返回Data数据。所以Ping的发起方把时间放到了Data数据里,当得到Echo Reply里,取到发送时间。再和当前时间比較,就能够得到耗时了。

当然,还有其他的思路,比方记录每个包的发送时间。当得到返回时。再计算得到时间差,但显然这种实现太复杂了。

Ping怎样区分不同的进程?

我们都知道本机IP。远程IP,本机port,远程port,四个元素才干够确定唯的一个信道。而ICMP里没有port,那么一个ping程序怎样知道哪些包才是发给自己的?或者说操作系统怎样差别哪个Echo Reply是要发给哪个进程的?

实际上操作系统不能差别,全部的本机IP。远程IP同样的ICMP程序都能够接收到同一份数据。

程序自己要依据Identifier来区分究竟一个ICMP包是不是发给自己的。在Linux下,Ping发出去的Echo Request包里Identifier就是进程pid,远程主机会返回一个Identifier同样的Echo Reply包。

能够接以下的方法简单验证:

启动系统自带的ping程序,查看其pid。

设定自己实现的ping程序的identifier为上面得到的pid。然后发Echo Request包。

能够发现系统ping程序会接收到远程主机的回应。

自己实现ping

自己实现ping要用到rawsocket,在linux下须要root权限。

网上有非常多实现的程序,可是有非常多地方不太对的。自己总结实现了一个(最好用g++编绎):

#include 
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
unsigned short csum(unsigned short *ptr, int nbytes) { register long sum; unsigned short oddbyte; register short answer; sum = 0; while (nbytes > 1) { sum += *ptr++; nbytes -= 2; } if (nbytes == 1) { oddbyte = 0; *((u_char*) &oddbyte) = *(u_char*) ptr; sum += oddbyte; } sum = (sum >> 16) + (sum & 0xffff); sum = sum + (sum >> 16); answer = (short) ~sum; return (answer);}inline double countMs(timeval before, timeval after){ return (after.tv_sec - before.tv_sec)*1000 + (after.tv_usec - before.tv_usec)/1000.0;}#pragma pack(1)struct EchoPacket { u_int8_t type; u_int8_t code; u_int16_t checksum; u_int16_t identifier; u_int16_t sequence; timeval timestamp; char data[40]; //sizeof(EchoPacket) == 64};#pragma pack()void ping(in_addr_t source, in_addr_t destination) { static int sequence = 1; static int pid = getpid(); static int ipId = 0; char sendBuf[sizeof(iphdr) + sizeof(EchoPacket)] = { 0 }; struct iphdr* ipHeader = (iphdr*)sendBuf; ipHeader->version = 4; ipHeader->ihl = 5; ipHeader->tos = 0; ipHeader->tot_len = htons(sizeof(sendBuf)); ipHeader->id = htons(ipId++); ipHeader->frag_off = htons(0x4000); //set Flags: don't fragment ipHeader->ttl = 64; ipHeader->protocol = IPPROTO_ICMP; ipHeader->check = 0; ipHeader->saddr = source; ipHeader->daddr = destination; ipHeader->check = csum((unsigned short*)ipHeader, ipHeader->ihl * 2); EchoPacket* echoRequest = (EchoPacket*)(sendBuf + sizeof(iphdr)); echoRequest->type = 8; echoRequest->code = 0; echoRequest->checksum = 0; echoRequest->identifier = htons(pid); echoRequest->sequence = htons(sequence++); gettimeofday(&(echoRequest->timestamp), NULL); u_int16_t ccsum = csum((unsigned short*)echoRequest, sizeof(sendBuf) - sizeof(iphdr)); echoRequest->checksum = ccsum; struct sockaddr_in sin; sin.sin_family = AF_INET; sin.sin_port = htons(0); sin.sin_addr.s_addr = destination; int s = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP); if (s == -1) { perror("socket"); return; } //IP_HDRINCL to tell the kernel that headers are included in the packet if (setsockopt(s, IPPROTO_IP, IP_HDRINCL, "1",sizeof("1")) < 0) { perror("Error setting IP_HDRINCL"); exit(0); } sendto(s, sendBuf, sizeof(sendBuf), 0, (struct sockaddr *) &sin, sizeof(sin)); char responseBuf[sizeof(iphdr) + sizeof(EchoPacket)] = {0}; struct sockaddr_in receiveAddress; socklen_t len = sizeof(receiveAddress); int reveiveSize = recvfrom(s, (void*)responseBuf, sizeof(responseBuf), 0, (struct sockaddr *) &receiveAddress, &len); if(reveiveSize == sizeof(responseBuf)){ EchoPacket* echoResponse = (EchoPacket*) (responseBuf + sizeof(iphdr)); //TODO check identifier == pid ? if(echoResponse->type == 0){ struct timeval tv; gettimeofday(&tv, NULL); in_addr tempAddr; tempAddr.s_addr = destination; printf("%d bytes from %s : icmp_seq=%d ttl=%d time=%.2f ms\n", sizeof(EchoPacket), inet_ntoa(tempAddr), ntohs(echoResponse->sequence), ((iphdr*)responseBuf)->ttl, countMs(echoResponse->timestamp, tv)); }else{ printf("response error, type:%d\n", echoResponse->type); } }else{ printf("error, response size != request size.\n"); } close(s);}int main(void) { in_addr_t source = inet_addr("192.168.1.100"); in_addr_t destination = inet_addr("192.168.1.1"); for(;;){ ping(source, destination); sleep(1); } return 0;}

安全相关的一些东东:

死亡之Ping  http://zh.wikipedia.org/wiki/%E6%AD%BB%E4%BA%A1%E4%B9%8BPing

虽然是非常老的漏洞。可是也能够看出协议栈的实现也不是那么的靠谱。

Ping flood   http://en.wikipedia.org/wiki/Ping_flood

server关闭ping服务,默认是0,是开启:

echo 1 > /proc/sys/net/ipv4/icmp_echo_ignore_all

总结:

在自己实现的过程中。发现有一些蛋疼的地方,如

协议文档不够清晰,得重复对比。

有时候一个小地方处理不正确,非常难查bug。即使程序能正常工作,但也并不代表它是正确的。

用wireshark能够非常方便验证自己写的程序有没有问题。

參考:

http://en.wikipedia.org/wiki/Ping_(networking_utility)

http://en.wikipedia.org/wiki/ICMP_Destination_Unreachable

http://tools.ietf.org/pdf/rfc792.pdf

转载地址:http://ututo.baihongyu.com/

你可能感兴趣的文章
数组相关知识
查看>>
1设计模式---工厂模式。
查看>>
HDU-1573 X问题(中国剩余定理)
查看>>
UNIX环境高级编程——IPC总结
查看>>
UNIX网络编程——处理服务器中大量的TIME_WAIT
查看>>
添物不花钱学计算机及编程(预备篇) - 软件工程
查看>>
带状态论文粗读(三)[引用openstate的相关论文阅读]
查看>>
pcDuino无显示器刷机与使用
查看>>
程序员出路在何方
查看>>
linux-alias基本用法
查看>>
compose函数
查看>>
Professional C# 6 and .NET Core 1.0 - Chapter 39 Windows Services
查看>>
C# 6 与 .NET Core 1.0 高级编程 - 40 ASP.NET Core(上)
查看>>
(已解决)Xcode 运行cocos2dx弹出内部错误对话框(Internal Error)
查看>>
J2EE 13规范(2)-JNDI
查看>>
模板维护-模板测试
查看>>
django -- 对模式进行调式(pay with the api)
查看>>
SQL Server sp_configure 控制内存使用
查看>>
通读《构建之法》提出问题
查看>>
VB生成xml
查看>>