首页 > 系统相关 >如何编码实现Windows下的ping功能

如何编码实现Windows下的ping功能

时间:2023-01-04 14:33:15浏览次数:47  
标签:编码 echoReq short Windows ping char int nRet 回送

一、数据结构
首先根据IP数据包格式(图下图)定义IP数据包头的数据结构
typedef struct tagIPHDR // IP数据包头部
{
u_char VIHL; // 版本号(4)+头长度(4)
u_char TOS; // 服务类型(8)
short TotLen; // 总长度(16)
short ID; // 标识(16)
short FlagOff; // 标志(3)+片偏移(13)
u_char TTL; // 生存时间TTL(8)
u_short CheckSum; // 头部校验和(16)
in_addr iaSrc; // 源IP地址(32)
in_addr iaDst; // 目标IP地址(32)
} IPHDR, *PIPHDR;
然后根据ICMP回送请求与应答报文格式定义ICMP的数据结构
typedef struct tagICMPHDR // ICMP回送请求与应带ICMP报文
{
u_char Type; // 类型(8)
u_char Code; // 代码(8)
u_short Checksum; // 校验和(16)
u_short ID; // 标识符(16)
u_short Seq; // 序号(16)
char Data; // 任选数据
} ICMPHDR, *PICMPHDR;

然后分别定义请求回送的数据长度
#define REQ_DATASIZE 32
请求回送的数据结构
typedef struct tagECHOREQUEST
{
ICMPHDR icmpHdr;
DWORD dwTime;
char cData[REQ_DATASIZE];
} ECHOREQUEST, *PECHOREQUEST;
ICMP回送应答的数据结构
typedef struct tagECHOREPLY
{
IPHDR ipHdr;
ECHOREQUEST echoRequest;
char cFiller[256];
} ECHOREPLY, *PECHOREPLY;

二、函数实现
(1)SendEchoRequest
函数功能是发送回送请求数据包,首先定义三个静态变量
static ECHOREQUEST echoReq; // 回送请求数据结构
static nId = 1; // 标识符
static nSeq = 1; // 序号然后填写回送请求信息
echoReq.icmpHdr.Type = ICMP_ECHOREQ; // 类型
echoReq.icmpHdr.Code = 0; // 代码
echoReq.icmpHdr.Checksum = 0; // 校验和
echoReq.icmpHdr.ID = nId++; // 标识符 echoReq.icmpHdr.Seq = nSeq++; // 序号
填写要发送的数据
for (i = 0; i < REQ_DATASIZE; i++)
{
echoReq.cData[i] = ' ' + i;
}保存发送时间
echoReq.dwTime = GetTickCount(); 数据存入包中并计算校验和
echoReq.icmpHdr.Checksum = in_chsum((u_short*)&echoReq, sizeof(ECHOREQUEST));发送回送请求
nRet = sendto(s,
(LPSTR)&echoReq,
sizeof(ECHOREQUEST),
0,
(LPSOCKADDR)lpstToAddr,
sizeof(SOCKADDR_IN));(2)RecvEchoReply
函数功能为接收回送应答数据


DWORD RecvEchoReply(SOCKET s, LPSOCKADDR_IN lpsaFrom, u_char *pTTL)
{
ECHOREPLY echoReply; // 回送应答数据结构
int nRet;
int nAddrLen = sizeof(sockaddr_in);
// 接受回送应答
nRet = recvfrom(s,
(LPSTR)&echoReply,
sizeof(ECHOREPLY),
0,
(LPSOCKADDR)lpsaFrom,
&nAddrLen);
// 检查返回的值
if (nRet == SOCKET_ERROR)
{
ReportError("recvfrom()");
}
*pTTL = echoReply.ipHdr.TTL; // 取得TTL值
return (echoReply.echoRequest.dwTime); // 返回所用时间
}
(3)WaitForEchoReply函数功能:等待套接子s是否有数据可读
int WaitForEchoReply(SOCKET s)
{
timeval Timeout;
fd_set readfds;
readfds.fd_count = 1;
readfds.fd_array[0] = s;
Timeout.tv_sec = 5;
Timeout.tv_usec = 0;
return (select(1, &readfds, NULL, NULL, &Timeout));
}
(3)in_chsum
函数功能计算校验和
u_short in_chsum(u_short *addr, int len)
{
register int nLeft = len;
register u_short *w = addr;
register u_short answer;
register int sum = 0;
while (nLeft > 1)
{
sum += *w++;
nLeft -= 2;
}

if (nLeft == 1)
{
u_short u = 0;
*(u_char*)(&u) = *(u_char*)w;
sum += u;
}

sum = (sum >> 16) + (sum & 0xffff);
sum += (sum >> 16);
answer = ~sum;
return (answer);
}(4)main函数的实现
第一步:定义Winsock数据结构wsaData并新建版本号1.1
第二步:调用WSAStartup初始化wsaData
第三步:调用Ping函数
第四步:调用WSACleanup释放Winsock
void main(int argc, char **argv)
{
WSADATA wsaData;
WORD wVersionRequested = MAKEWORD(1, 1); // Winsock1.1
int nRet;

// 命令行参数检查
if (argc != 2)
{
fprintf(stderr, "/nUsage: ping hostname/n");
return;
}

// 初始化Winsock
nRet = WSAStartup(wVersionRequested, &wsaData);
if (nRet)
{
fprintf(stderr, "/nError initializing Winsock/n");
return;
}

if (wsaData.wVersion != wVersionRequested)
{
fprintf(stderr, "/nWinsock version not supported/n");
return;
}
// 调用ping函数
Ping(argv[1]);
//Ping("www.sina.com");
// 释放Winsock
WSACleanup();
}(5)Ping
函数功能:实现ping功能
定义函数用到的数据
SOCKET rawSocket; // 原始套接字
LPHOSTENT lpHost; // 主机信息
sockaddr_in saDest; // 目的地址
sockaddr_in saSrc; // 源地址
DWORD dwTimeSent; // 发送时间
DWORD dwElapsed; // 延迟时间然后创建一个原始套接字
创建一个原始套接口,协议为ICMP协议
rawSocket = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);根据用户输入的目的地址获取
lpHost = gethostbyname(pstrHost);
设置目标套接口地址
saDest.sin_addr.s_addr = *((u_long FAR*)(lpHost->h_addr));
saDest.sin_family = AF_INET;
saDest.sin_port = 0; 输出ping程序的提示信息
printf("/nPinging %s [%s] with %d bytes of data:/n",
pstrHost,
inet_ntoa(saDest.sin_addr),
REQ_DATASIZE); 发送ICMP回送请求
SendEchoRequest(rawSocket, &saDest);
使用select()等待接收回送的数据 WaitForEchoReply(rawSocket);
接收应答
dwTimeSent = RecvEchoReply(rawSocket, &saSrc, &cTTL);
计算传输时间,并输出提示信息
dwElapsed = GetTickCount() - dwTimeSent;答应应答信息
printf("/nReply from: %s: bytes=%d time=%ldms TTL=%d",
inet_ntoa(saSrc.sin_addr),
REQ_DATASIZE,
dwElapsed,
cTTL);
}
// 关闭套接字
nRet = closesocket(rawSocket);附:程序源代码
// ping.h
// 在该头文件中定义了IP和ICMP协议头的结构
#pragma pack(1)
#define ICMP_ECHOREPLY 0
#define ICMP_ECHOREQ 8
typedef struct tagIPHDR // IP数据包头部
{
u_char VIHL; // 版本号(4)+头长度(4)
u_char TOS; // 服务类型(8)
short TotLen; // 总长度(16)
short ID; // 标识(16)
short FlagOff; // 标志(3)+片偏移(13)
u_char TTL; // 生存时间TTL(8)
u_short CheckSum; // 头部校验和(16)
in_addr iaSrc; // 源IP地址(32)
in_addr iaDst; // 目标IP地址(32)
} IPHDR, *PIPHDR;
typedef struct tagICMPHDR // ICMP回送请求与应带ICMP报文
{
u_char Type; // 类型(8)
u_char Code; // 代码(8)
u_short Checksum; // 校验和(16)
u_short ID; // 标识符(16)
u_short Seq; // 序号(16)
char Data; // 任选数据
} ICMPHDR, *PICMPHDR;
// 请求回送的数据长度
#define REQ_DATASIZE 32
// ICMP回送请求的数据结构
typedef struct tagECHOREQUEST
{
ICMPHDR icmpHdr;
DWORD dwTime;
char cData[REQ_DATASIZE];
} ECHOREQUEST, *PECHOREQUEST;
// ICMP回送应答
typedef struct tagECHOREPLY
{
IPHDR ipHdr;
ECHOREQUEST echoRequest;
char cFiller[256];
} ECHOREPLY, *PECHOREPLY;
#pragma pack()

// ping.cpp
// 实现简易ping功能
#include <stdio.h>
#include <stdlib.h>
#include <winsock.h>
#include "ping.h"
void Ping(LPCSTR pstrHost);
void ReportError(LPCSTR pstrFrom);
int WaitForEchoReply(SOCKET s);
u_short in_chsum(u_short *addr, int len);
// ICMP 回送请求和应答函数声明
int SendEchoRequest(SOCKET, LPSOCKADDR_IN);
DWORD RecvEchoReply(SOCKET, LPSOCKADDR_IN, u_char *);
// 主程序
void main(int argc, char **argv)
{
WSADATA wsaData;
WORD wVersionRequested = MAKEWORD(1, 1); // Winsock1.1
int nRet;

// 命令行参数检查
if (argc != 2)
{
fprintf(stderr, "/nUsage: ping hostname/n");
return;
}
// 初始化Winsock
nRet = WSAStartup(wVersionRequested, &wsaData);
if (nRet)
{
fprintf(stderr, "/nError initializing Winsock/n");
return;
}
if (wsaData.wVersion != wVersionRequested)
{
fprintf(stderr, "/nWinsock version not supported/n");
return;
}
// 调用ping函数
Ping(argv[1]);
//Ping("www.sina.com");
// 释放Winsock
WSACleanup();
}
void Ping(LPCSTR pstrHost)
{
SOCKET rawSocket; // 原始套接字
LPHOSTENT lpHost; // 主机信息
sockaddr_in saDest; // 目的地址
sockaddr_in saSrc; // 源地址
DWORD dwTimeSent; // 发送时间
DWORD dwElapsed; // 延迟时间
u_char cTTL;
int nLoop;
int nRet;
// 创建一个原始套接口
rawSocket = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);
if (rawSocket == SOCKET_ERROR)
{
ReportError("socket()");
return;
}
lpHost = gethostbyname(pstrHost);
if (lpHost == NULL)
{
fprintf(stderr, "/nHost not found: %s/n", pstrHost);
return;
}
// 设置目标套接口地址
saDest.sin_addr.s_addr = *((u_long FAR*)(lpHost->h_addr));
saDest.sin_family = AF_INET;
saDest.sin_port = 0;
// 输出ping程序的提示信息
printf("/nPinging %s [%s] with %d bytes of data:/n",
pstrHost,
inet_ntoa(saDest.sin_addr),
REQ_DATASIZE);
// 控制ping执行的次数
for (nLoop = 0; nLoop < 4; nLoop++)
{
// 发送ICMP回送请求
SendEchoRequest(rawSocket, &saDest);
// 使用select()等待接收回送的数据
nRet = WaitForEchoReply(rawSocket);
if (nRet == SOCKET_ERROR)
{
ReportError("select()");
break;
}
if (!nRet)
{
printf("/nTimeOut/n");
break;
}
// 接收应答
dwTimeSent = RecvEchoReply(rawSocket, &saSrc, &cTTL);
// 计算传输时间,并输出提示信息
dwElapsed = GetTickCount() - dwTimeSent;
printf("/nReply from: %s: bytes=%d time=%ldms TTL=%d",
inet_ntoa(saSrc.sin_addr),
REQ_DATASIZE,
dwElapsed,
cTTL);
}
printf("/n");
// 关闭套接字
nRet = closesocket(rawSocket);
if (nRet == SOCKET_ERROR)
{
ReportError("closesocket()");
}
}
int SendEchoRequest(SOCKET s, LPSOCKADDR_IN lpstToAddr)
{
static ECHOREQUEST echoReq; // 回送请求数据结构
static nId = 1; // 标识符
static nSeq = 1; // 序号
int nRet;
int i;
// 填写回送请求信息
echoReq.icmpHdr.Type = ICMP_ECHOREQ; // 类型
echoReq.icmpHdr.Code = 0; // 代码
echoReq.icmpHdr.Checksum = 0; // 校验和
echoReq.icmpHdr.ID = nId++; // 标识符
echoReq.icmpHdr.Seq = nSeq++; // 序号
// 填写要发送的数据
for (i = 0; i < REQ_DATASIZE; i++)
{
echoReq.cData[i] = ' ' + i;
}
// 保存发送时间
echoReq.dwTime = GetTickCount();
// 数据存入包中并计算校验和
echoReq.icmpHdr.Checksum = in_chsum((u_short*)&echoReq, sizeof(ECHOREQUEST));
// 发送回送请求
nRet = sendto(s,
(LPSTR)&echoReq,
sizeof(ECHOREQUEST),
0,
(LPSOCKADDR)lpstToAddr,
sizeof(SOCKADDR_IN));
if (nRet == SOCKET_ERROR)
{
ReportError("sendto()");
}
return nRet;
}
DWORD RecvEchoReply(SOCKET s, LPSOCKADDR_IN lpsaFrom, u_char *pTTL)
{
ECHOREPLY echoReply; // 回送应答数据结构
int nRet;
int nAddrLen = sizeof(sockaddr_in);
// 接受回送应答
nRet = recvfrom(s,
(LPSTR)&echoReply,
sizeof(ECHOREPLY),
0,
(LPSOCKADDR)lpsaFrom,
&nAddrLen);
// 检查返回的值
if (nRet == SOCKET_ERROR)
{
ReportError("recvfrom()");
}
*pTTL = echoReply.ipHdr.TTL; // 取得TTL值
return (echoReply.echoRequest.dwTime); // 返回所用时间
}
void ReportError(LPCSTR pstrFrom)
{
fprintf(stderr, "/n %d error: /n", WSAGetLastError());
}
// 等待套接子s是否有数据可读
int WaitForEchoReply(SOCKET s)
{
timeval Timeout;
fd_set readfds;
readfds.fd_count = 1;
readfds.fd_array[0] = s;
Timeout.tv_sec = 5;
Timeout.tv_usec = 0;
return (select(1, &readfds, NULL, NULL, &Timeout));
}
u_short in_chsum(u_short *addr, int len)
{
register int nLeft = len;
register u_short *w = addr;
register u_short answer;
register int sum = 0;
while (nLeft > 1)
{
sum += *w++;
nLeft -= 2;
}
if (nLeft == 1)
{
u_short u = 0;
*(u_char*)(&u) = *(u_char*)w;
sum += u;
}
sum = (sum >> 16) + (sum & 0xffff);
sum += (sum >> 16);
answer = ~sum;
return (answer);
}

标签:编码,echoReq,short,Windows,ping,char,int,nRet,回送
From: https://blog.51cto.com/u_15929756/5988623

相关文章

  • 调整 Windows 11 任务栏位置
    原文链接:如何调整Windows11任务栏位置、对齐方式,及自定义任务栏(sysgeek.cn)将Windows11任务栏调到桌面顶部、左边或右边如果只将任务栏图标「靠左」和「居中」对......
  • Windows中IIS7设置上传文件大小限制
    IIS7默认文件上传大小时限制30M。要突破这个限制:1.修改IIS的applicationhost.config打开%windir%\system32\inetsrv\config\applicationhost.config找到:<requestFilter......
  • 变分自编码器 - VAE: Variational Auto-Encoder
    总之,VAE本身是一个生成模型,我们假设观测的某个变量\(\mathbf{x}\)(比如数字0~9的各种图像)受到隐变量\(\mathbf{z}\)的影响,那么在得到分布后,只需要采样得到一个\(\mat......
  • Windows xp 64 bit
    WindowsXPProfessionalx64EditionwithSP2-VL(English) 详细信息文件名en_win_xp_pro_x64_with_sp2_vl_X13-41611.isoSHA1CD9479E1DBAD7F26B8BDCF97E4AA71CBB8DE......
  • windows自动开关机命令
    Windows系统自带了一个名为shutdown.exe的程序,这个程序默认是在C:WindowsSystem32这个文件夹下面,一般情况下Windows系统的关机都可以通过调用程序shutdown.exe来实现的,同时......
  • postgresql windows 开发环境搭建
    postgresqlwindows开发环境搭建发布时间:2020-06-2912:45:05 来源:网络 阅读:2883 作者:pgmia 栏目:数据库一、软件需求Windows7旗舰版sp1X64ActivePerl-5.......
  • Windows下用VS2022编译安装Boost库
    Windows下用VS2022编译安装Boost库下载地址:https://www.boost.org/users/download/解压得到如下文件:  编译安装:打开vs2022命令行工具  切换到boost的目......
  • 注入—Module Stomping注入
    ModuleStomping注入ModuleStomping(又称为ModuleOverloading,又称为DLLHollowing)技术工作原理:将一些正常DLL注入到目标进程中,寻找AddressOfEntryPoint,并使用Shellcode......
  • 【拓展】745- Windows Server 升级 HTTPS 实战
    趁着国庆假期,开发了个小程序,但需要服务器使用HTTPS协议,因此借这次机会,学着自己完成HTTPS升级工作。如有什么错误,欢迎指正!......
  • Game——Fix the “You’ll Need a New App to Open This Ms-Gamingoverlay” Error o
    参考链接win11我用的方法6解决。......