### 编程实现Ping命令详解 #### 实验名称: 编程实现Ping命令 #### 实验要求: 1. 使用C语言在Windows环境下实现一个简单的Ping程序。 2. 该程序能够发送ICMP请求包,并接收响应包。 3. 显示每个请求与响应的时间延迟,并给出统计数据。 #### 实验环境: - 操作系统:Windows - 开发工具:Visual Studio 或其他支持WinSock编程的IDE - 编程语言:C/C++ - 库:WinSock2.h、stdlib.h、stdio.h #### 实验步骤及代码解析: **1. 引入必要的头文件** ```c #include "winsock2.h" #include "stdlib.h" #include "stdio.h" ``` 这里引入了WinSock2库,用于进行网络编程;stdlib.h提供了内存分配函数;stdio.h则用于标准输入输出。 **2. 定义常量** ```c #define ICMP_ECHO 8 #define ICMP_ECHOREPLY 0 #define ICMP_MIN 8 // 最小8字节ICMP包(仅头部) ``` 这些宏定义分别表示ICMP Echo请求类型(8)、ICMP Echo响应类型(0)以及最小的ICMP包大小(8字节)。 **3. 定义结构体** ```c typedef struct iphdr { unsigned int h_len:4; // 头部长度 unsigned int version:4; // IP版本 unsigned char tos; // 服务类型 unsigned short total_len; // 包总长度 unsigned short ident; // 唯一标识符 unsigned short frag_and_flags; // 分片标志 unsigned char ttl; // 生存时间 unsigned char proto; // 协议类型 unsigned short checksum; // IP校验和 unsigned int sourceIP; // 源IP地址 unsigned int destIP; // 目标IP地址 } IpHeader; // ICMP头部 typedef struct icmphdr { BYTE i_type; // 类型 BYTE i_code; // 子类型代码 USHORT i_cksum; // 校验和 USHORT i_id; // 标识符 USHORT i_seq; // 序列号 ULONG timestamp; // 时间戳 } IcmpHeader; ``` 这里定义了两个结构体`iphdr`和`icmphdr`,分别表示IP头部和ICMP头部的信息。 **4. 定义宏与辅助函数** ```c #define STATUS_FAILED 0xFFFF #define DEF_PACKET_SIZE 32 #define DEF_PACKET_NUMBER 4 #define MAX_PACKET 1024 #define xmalloc(s) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, (s)) #define xfree(p) HeapFree(GetProcessHeap(), 0, (p)) void fill_icmp_data(char *, int); USHORT checksum(USHORT *, int); int decode_resp(char *, int, struct sockaddr_in *); ``` 这些宏定义用于设置包的大小、数量等参数。辅助函数包括填充ICMP数据、计算校验和以及解码响应等功能。 **5. 主函数实现** ```c int main(int argc, char **argv) { WSADATA wsaData; SOCKET sockRaw; struct sockaddr_in dest, from; struct hostent *hp; int bread, datasize, times; int fromlen = sizeof(from); int timeout = 1000; int statistic = 0; char *dest_ip; char *icmp_data; char *recvbuf; unsigned int addr = 0; USHORT seq_no = 0; // 初始化WinSock if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) { printf("WSAStartup failed with error: %d\n", WSAGetLastError()); return -1; } // 创建原始套接字 sockRaw = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP); if (sockRaw == INVALID_SOCKET) { printf("Socket creation failed with error: %d\n", WSAGetLastError()); WSACleanup(); return -1; } // 设置目标地址 dest.sin_family = AF_INET; dest.sin_port = 0; dest.sin_addr.s_addr = inet_addr(dest_ip); // 循环发送ICMP请求 for (times = 0; times < DEF_PACKET_NUMBER; times++) { icmp_data = (char *)xmalloc(DEF_PACKET_SIZE + sizeof(IcmpHeader)); fill_icmp_data(icmp_data, DEF_PACKET_SIZE); // 发送ICMP请求 if (send(sockRaw, icmp_data, DEF_PACKET_SIZE + sizeof(IcmpHeader), 0) == SOCKET_ERROR) { printf("Send failed with error: %d\n", WSAGetLastError()); break; } // 接收ICMP响应 recvbuf = (char *)xmalloc(MAX_PACKET); bread = recv(sockRaw, recvbuf, MAX_PACKET, 0); if (bread > 0) { // 解析响应 int resp = decode_resp(recvbuf, bread, &from); if (resp > 0) { printf("Received echo reply from %s: bytes=%d time=%dms TTL=%d\n", inet_ntoa(from.sin_addr), bread, resp, resp); statistic++; } } else { printf("Receive failed with error: %d\n", WSAGetLastError()); } xfree(icmp_data); xfree(recvbuf); } closesocket(sockRaw); WSACleanup(); // 输出统计信息 printf("Packets: Sent = %d, Received = %d, Lost = %d (%d%% loss)\n", DEF_PACKET_NUMBER, statistic, DEF_PACKET_NUMBER - statistic, (DEF_PACKET_NUMBER - statistic) * 100 / DEF_PACKET_NUMBER); return 0; } ``` 主函数首先初始化WinSock环境,然后创建原始套接字并配置目标地址。接下来进入循环,依次发送ICMP请求,并接收响应。最后关闭套接字并清理WinSock环境。 **6. 辅助函数实现** ```c void fill_icmp_data(char *buf, int size) { // 填充ICMP数据 } USHORT checksum(USHORT *buf, int len) { // 计算ICMP包的校验和 } int decode_resp(char *buf, int len, struct sockaddr_in *from) { // 解析ICMP响应 } ``` 这些辅助函数用于填充ICMP数据、计算校验和以及解析响应。 #### 实验体会: 通过本次实验,我们深入了解了ICMP协议的工作原理,掌握了如何使用WinSock API进行网络编程。此外,还学习了如何在Windows环境下构建、发送和接收ICMP包,这对于理解网络通信机制非常有帮助。 这次实验不仅加深了对网络基础知识的理解,也提高了实际编程能力。通过亲手编写代码,我们能够更加直观地感受到网络通信的具体过程,这对于未来从事网络开发或网络安全领域的工作都有很大的帮助。
- 粉丝: 103
- 资源: 6万+
- 我的内容管理 展开
- 我的资源 快来上传第一个资源
- 我的收益 登录查看自己的收益
- 我的积分 登录查看自己的积分
- 我的C币 登录后查看C币余额
- 我的收藏
- 我的下载
- 下载帮助