Ping命令的实现

这篇具有很好参考价值的文章主要介绍了Ping命令的实现。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

Ping (Packet Internet Groper)是一种因特网包探索器,用于测试网络连接量的程序。本文将基于 Socket 编程,实现一个基本的 Ping 命令程序。

ICMP 报文分析

ICMP 报文捕获

在控制台输入 ping 202.195.147.248,对该目的主机发起请求,可以看到控制台输出了一系列统计信息:4 个数据包全部接收并且往返时间为 5 ms(较短),表明与该主机之间的连接畅通。

Ping命令的实现

使用 Wireshark 工具捕获 icmp 数据包,为了避免无关数据包的干扰,可以使用 filter 对数据包进行过滤,在上部栏输入 ip.src == 202.195.147.248 or ip.dst == 202.195.147.248,表明只筛选源地址或目的地址为 202.195.147.248 的数据包,最终可以得到数据包的内容。

Ping命令的实现

Wireshark 数据包分析

根据 ICMP 报文的格式进行分析:

  1. Type:数据包类型,占 1 Byte,为 0x00,代表回送报文。
  2. Code:代码部分,占 1 Byte,为 0x00.
  3. Checksum:检验和,占 2 Bytes,为 0x554c.
  4. Identifier(IE):占 2 Bytes,为 0x0001.
  5. Identifier(LE):占 2 Bytes,为 0x0100.
  6. Sequence Number(BE):占 2 Bytes,为 0x000f.
  7. Sequence Number(LE):占 2 Bytes,为 0x0f00.
  8. Data:占 32 Bytes,为6162636465666768696a6b6c6d6e6f7071727374757677616263646566676869.

实现思路

构造 ICMP 报文

自定义数据结构 icmpHeader 表示 ICMP 报文头部,包含类型、代码、检验和、标识符和序列号。

// ICMP 报文头
struct icmpHeader {
	unsigned char type; // 类型
	unsigned char code; // 代码
	unsigned short checkSum; // 检验和
	unsigned short id; // 标识符
	unsigned short sequence; // 序列号
};

填充该报文,类型为 8 表示请求报文。检验和使用特定的算法计算,关于算法的具体内容可以自行查看相关文档,在此不过多赘述。标识符使用进程 id 填充。最后在 ICMP 报文头的尾部,添加 32 字节的数据作为 ICMP 报文的数据部分。

// 构造 ICMP 报文
char sendBuf[8 + 32] = { 0 };
icmpHeader* pIcmp = (icmpHeader*)sendBuf;
pIcmp->type = 8;
pIcmp->code = 0;
pIcmp->checkSum = 0;
pIcmp->id = (USHORT)::GetCurrentProcessId();
pIcmp->sequence = 0;
// 填充数据部分
memcpy(sendBuf + 8, "abcdelmnopqrstuvwiammekakuactor", 32);
// 计算检验和
pIcmp->checkSum = computeCks((icmpHeader*)sendBuf, sizeof(sendBuf));

发送请求报文

该部分使用 Socket 编程向指定 IP 地址发送 ICMP 请求报文。需要注意的是,在创建套接字时,需要使用原始套接字,且 protocol 参数为 IPPROTO_ICMP,表明使用 ICMP 协议。SOCKET s = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP); 报文发送成功后,接收从客户端发送的回送报文信息。

// 初始化套接字库
WORD wReq = MAKEWORD(2, 2);
WSADATA wsadata;
WSAStartup(wReq, &wsadata);
// 填充服务端地址
SOCKADDR_IN serverAddr;
memset(&serverAddr, 0, sizeof(serverAddr));
serverAddr.sin_family = AF_INET;
serverAddr.sin_addr.S_un.S_addr = inet_addr(targetIP.c_str());
// 创建套接字
SOCKET s = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);

为了得到报文往返的时间,可以在发送前和接收后使用 GetTickCount64() 获取从操作系统启动到现在所经历的的时间 start 和 end,两时间相减得到时间差。

此外,由于 recvfrom() 在未收到报文时将会阻塞,因此可以使用 setsockopt() 设定一个接收超时时间,在超过指定时间未受到数据时返回 -1,表示接收异常。

setsockopt(s, SOL_SOCKET, SO_RCVTIMEO, (const char*)&timeOut, sizeof(timeOut)); // 设置接收超时

解析回送报文

根据回送的 IP 数据包的指定格式对信息进行解析,IPv4 头部的 4 到 8 位为 IP 报文头部长度,第 9 个字节是 TTL 的值。

完整代码

#include<iostream>
#include<string>
#include<winsock.h>
#include<thread>
#pragma comment(lib,"ws2_32.lib")

// ICMP 报文头
struct icmpHeader {
	unsigned char type; // 类型
	unsigned char code; // 代码
	unsigned short checkSum; // 检验和
	unsigned short id; // 标识符
	unsigned short sequence; // 序列号
};

// 计算检验和
unsigned short computeCks(icmpHeader* picmp, int len) {
	long sum = 0;
	unsigned short* pusicmp = (unsigned short*)picmp;
	while (len > 1) {
		sum += *(pusicmp++);
		if (sum & 0x80000000)
			sum = (sum & 0xffff) + (sum >> 16);
		len -= 2;
	}
	if (len)
		sum += (unsigned short)*(unsigned char*)pusicmp;
	while (sum >> 16)
		sum = (sum & 0xffff) + (sum >> 16);
	return (unsigned short)~sum;
}

int ping(const std::string& targetIP) {
	// 初始化套接字库
	WORD wReq = MAKEWORD(2, 2);
	WSADATA wsadata;
	WSAStartup(wReq, &wsadata);
	// 填充服务端地址
	SOCKADDR_IN serverAddr;
	memset(&serverAddr, 0, sizeof(serverAddr));
	serverAddr.sin_family = AF_INET;
	serverAddr.sin_addr.S_un.S_addr = inet_addr(targetIP.c_str());
	// 创建套接字
	SOCKET s = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);
	
	// 构造 ICMP 报文
	char sendBuf[8 + 32] = { 0 };
	icmpHeader* pIcmp = (icmpHeader*)sendBuf;
	pIcmp->type = 8;
	pIcmp->code = 0;
	pIcmp->checkSum = 0;
	pIcmp->id = (USHORT)::GetCurrentProcessId();
	pIcmp->sequence = 0;
	// 填充数据部分
	memcpy(sendBuf + 8, "abcdelmnopqrstuvwiammekakuactor", 32);
	// 计算检验和
	pIcmp->checkSum = computeCks((icmpHeader*)sendBuf, sizeof(sendBuf));
	
	// 发送报文
	DWORD start = GetTickCount64();
	int sendLen = sendto(s, sendBuf, sizeof(sendBuf), 0, (SOCKADDR*)&serverAddr, sizeof(SOCKADDR));
	if (sendLen < 0) printf("errno = %d\n", GetLastError());
	// 接收报文
	char recvBuf[1024];
	SOCKADDR_IN fromAddr;
	int fLen = sizeof(fromAddr);
	unsigned timeOut = 1000; // 超时时间
	setsockopt(s, SOL_SOCKET, SO_RCVTIMEO, (const char*)&timeOut, sizeof(timeOut)); // 设置接收超时
	while (true) {
		int len = recvfrom(s, recvBuf, 1024, 0, (SOCKADDR*)&fromAddr, &fLen);
		if (len < 0) {
			std::cout << "请求超时" << std::endl;
			return INT32_MAX;
		}
		else break;
	}
	DWORD end = GetTickCount64();
	DWORD timeSpan = end - start;

	// 回送报文解析
	char ipInfo = recvBuf[0];
	// ipv4 头部的第 9 个字节为 TTL 的值
	unsigned char ttl = recvBuf[8];
	int ipHeadLen = ((char)(ipInfo << 4) >> 4) * 4; // IP报文头部长度
	icmpHeader* icmpResp = (icmpHeader*)(recvBuf + ipHeadLen);
	if (icmpResp->type == 0) { //回显应答报文
		printf("来自 %s 的回复:字节=32 时间=%2dms TTL=%d\n",
			targetIP.c_str(), timeSpan, ttl);
		return timeSpan;
	}
	else {
		printf("请求超时。type = %d\n", icmpResp->type);
		return INT32_MAX;
	}
}

int main() {
	std::cout << "请输入目的IP地址:";
	std::string IP;
	std::cin >> IP;
	int maxTime = INT32_MIN, minTime = INT32_MAX, timeSum = 0, acpkgCnt = 0;
	printf("\n正在 Ping %s 具有 32 字节的数据:\n", IP.c_str());
	for (int i = 0; i < 4; ++i) {
		std::this_thread::sleep_for(std::chrono::seconds(1));
		int timeSpan = ping(IP);
		acpkgCnt += timeSpan != INT32_MAX;
		maxTime = max(maxTime, timeSpan);
		minTime = min(minTime, timeSpan);
		timeSum += timeSpan;
	}
	printf("\n%s 的 Ping 统计信息:\n", IP.c_str());
    printf("    数据包: 已发送 = 4,已接收 = %d,丢失 = %d (%d%% 丢失),\n",
		acpkgCnt, 4 - acpkgCnt, (4 - acpkgCnt) * 100 / 4);
	if (!acpkgCnt) return 0;
    printf("往返行程的估计时间(以毫秒为单位):\n");
    printf("    最短 = %dms,最长 = %dms,平均 = %dms\n", minTime, maxTime, timeSum / acpkgCnt);
}

运行结果

Ping命令的实现

可见,本地与该目的主机的连通性较好。

Ping命令的实现

可见,本地与该目的主机无法连通。文章来源地址https://www.toymoban.com/news/detail-447382.html

到了这里,关于Ping命令的实现的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 5.3.3 因特网的路由协议(三)OSPF协议

    5.3.3 因特网的路由协议(三)OSPF协议 前面我们学习了基于距离向量算法的路由信息协议RIP(5.3.2 因特网的路由协议(二)基于距离向量算法的RIP协议),为了克服RIP协议的局限性于是就有了新的内部网关协议OSPF协议,OSPF是Open Shortest Path First的缩写,又被成为开放式最短路径

    2024年02月09日
    浏览(26)
  • 【MOOC 测验】第1章 计算机网络和因特网

    1、主机或端系统通过(   )接入因特网。 A. ISP B. 调制解调器 C. 交换机 D. 路由器 P21:端系统(PC、智能手机、Web 服务器、电子邮件服务器等)经过一个接入 ISP 与因特网相连。 2、‌主机一般被划分为两类:客户和 (   ) 。 A. 端系统 B. 大型主机 C. 数据中心 D. 服务器 3、

    2024年02月11日
    浏览(26)
  • 【MOOC 作业】第1章 计算机网络和因特网

    不是标答也不是参考答案 仅从个人理解出发去做题 1、(20分) ‌试比较分组交换和电路交换的主要优缺点。 电路交换: 特点:在端系统间通信期间,预留了端系统间沿路径通信需要的资源。 优点:发送方可以以恒定的速率向接收方发送数据。 缺点:创建端到端电路和预留端

    2024年02月11日
    浏览(35)
  • 【网络奇遇记】我和因特网的初相遇2 —— 三种交换方式

    🌈个人主页: 聆风吟 🔥系列专栏: 网络奇遇记、数据结构 🔖少年有梦不应止于心动,更要付诸行动。     网络的核心部分由大量异构型网络和连接这些网络的路由器构成,为其边缘部分提供连通性和数据交换等服务。在核心部分起特殊作用的是 路由器 ,它是一种专

    2024年02月05日
    浏览(29)
  • Python 标准类库-因特网数据处理之Base64数据编码

    该模块提供将二进制数据编码为可打印ASCII字符并将这种编码解码回二进制数据的功能。它为RFC 3548中指定的编码提供编码和解码功能。定义了Base16、Base32和Base64算法,以及事实上的标准Ascii85和Base85编码。 RFC 3548 编码适用于对二进制数据进行编码,以便可以安全地通过电子邮

    2024年02月06日
    浏览(37)
  • 【计算机网络 - 自顶向下方法】计算机网络和因特网

    1. 什么是Internet 1.1 因特网的具体构成 终端: 也称主机(Host)或端系统(End system),运行应用程序(智能网约汽车、智能家电)。 通信链路: 光纤,铜线,电磁波。主要指标为传输速率也称带宽(Bandwidth),可分为有线链路和无线链路。 交换设备: 转发分组(Packet),有

    2024年02月07日
    浏览(35)
  • 5.3.2 因特网的路由协议(二)基于距离向量算法的RIP协议

    5.3.2 因特网的路由协议(二)基于距离向量算法的RIP协议 一、RIP协议概念 RIP是Routing Information Protocol缩写,又称为路由信息协议,是最先得到应用的内部网关协议,RIP作为一个常在小型互联网中使用的路由信息协议,它是依据 跳数 来作为度量值进行路由选择,这里的跳数可

    2024年02月09日
    浏览(28)
  • 【网络奇遇记】我和因特网的初相遇3 —— 网络的体系结构篇

    🌈个人主页: 聆风吟 🔥系列专栏: 网络奇遇记、数据结构 🔖少年有梦不应止于心动,更要付诸行动。     为了使不同体系结构的计算机网络都能互连起来,国际标准化组织(简称:ISO)于1977年成立了专门机构研究该问题。不久,它们提出了一个使全世界各种计算机

    2024年02月04日
    浏览(33)
  • 【计算机网络 01】说在前面 信息服务 因特网 ISP RFC技术文档 边缘与核心 交换方式 定义与分类 网络性能指标 计算机网络体系结构 章节小结

    说在前面 本博客专栏都是基于B站上的湖科大计算机网络课程的课程笔记,主要是由于我发现无法从课程网站获得清晰PDF作为复习资料,所以制作此笔记同时也方便其他同学复习回顾,并且有少量补充参考 https://www.jianshu.com/u/5807cd8caf1d 同学的笔记 1.1 计算机网络 信息时代作用

    2024年02月16日
    浏览(37)
  • linux ping命令原理与用法(ping指令)ICMP(Internet Control Message Protocol,互联网控制报文协议)TTL(Time to Live)数据包生存时间

    Ping是一种网络工具,用于测试主机之间的连通性。它基于ICMP(Internet Control Message Protocol,互联网控制报文协议)来发送探测包并接收响应。 Ping工具发送一个ICMP Echo Request(回显请求)消息到目标主机,目标主机收到请求后会返回一个ICMP Echo Reply(回显回复)消息作为响应。

    2024年02月04日
    浏览(33)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包