用c++代码实现ping程序完整版
时间: 2023-09-18 09:15:50 浏览: 289
完整版的Ping程序需要实现以下功能:
1. 发送ICMP报文
2. 接收ICMP回复报文
3. 计算和显示延迟(ping值)
以下是一个基于C++的完整版Ping程序的实现:
```cpp
#include <iostream>
#include <cstdlib>
#include <cstring>
#include <cstdio>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/ip_icmp.h>
#include <netdb.h>
#include <ctime>
using namespace std;
// ICMP报文的结构体
struct icmp_packet {
struct icmphdr header;
char message[64 - sizeof(struct icmphdr)];
};
// 计算两个时间点之间的时间差(毫秒)
double time_diff(const timespec& start, const timespec& end) {
double start_sec = (double)start.tv_sec + (double)start.tv_nsec / 1000000000.0;
double end_sec = (double)end.tv_sec + (double)end.tv_nsec / 1000000000.0;
return (end_sec - start_sec) * 1000.0;
}
// 发送ICMP报文
void send_packet(int sockfd, const sockaddr_in& dest_addr, int seq) {
icmp_packet packet;
memset(&packet, 0, sizeof(packet));
packet.header.type = ICMP_ECHO; // 设置报文类型为ICMP请求报文
packet.header.code = 0; // 设置代码为0
packet.header.un.echo.id = getpid(); // 设置进程ID作为标识符
packet.header.un.echo.sequence = seq; // 设置序列号
// 填充报文数据
const char* message = "Hello, world!";
strncpy(packet.message, message, sizeof(packet.message));
// 计算校验和
packet.header.checksum = 0;
packet.header.checksum = htons(~(htons(packet.header.checksum) + sizeof(packet.header) + sizeof(packet.message)));
// 发送报文
if (sendto(sockfd, &packet, sizeof(packet), 0, (const sockaddr*)&dest_addr, sizeof(dest_addr)) < 0) {
cerr << "Error: Failed to send ICMP packet." << endl;
exit(EXIT_FAILURE);
}
}
// 接收ICMP回复报文
bool receive_packet(int sockfd, const sockaddr_in& dest_addr, int seq, timespec& start_time) {
icmp_packet packet;
sockaddr_in src_addr;
socklen_t addrlen = sizeof(src_addr);
int n = recvfrom(sockfd, &packet, sizeof(packet), 0, (sockaddr*)&src_addr, &addrlen);
if (n < 0) {
cerr << "Error: Failed to receive ICMP packet." << endl;
return false;
}
// 检查来源地址和端口号
if (src_addr.sin_addr.s_addr != dest_addr.sin_addr.s_addr || src_addr.sin_port != dest_addr.sin_port) {
cerr << "Error: Received ICMP packet from unknown source." << endl;
return false;
}
// 检查ICMP类型和代码
if (packet.header.type != ICMP_ECHOREPLY || packet.header.code != 0) {
cerr << "Error: Received ICMP packet with unexpected type or code." << endl;
return false;
}
// 检查标识符和序列号
if (packet.header.un.echo.id != getpid() || packet.header.un.echo.sequence != seq) {
cerr << "Error: Received ICMP packet with unexpected ID or sequence number." << endl;
return false;
}
// 计算延迟并显示
timespec end_time;
clock_gettime(CLOCK_MONOTONIC, &end_time);
double delay = time_diff(start_time, end_time);
printf("%d bytes from %s: icmp_seq=%d ttl=%d time=%.2f ms\n", n, inet_ntoa(src_addr.sin_addr), seq, packet.header.un.echo.id, delay);
return true;
}
// Ping程序的入口函数
int main(int argc, char* argv[]) {
if (argc != 2) {
cerr << "Usage: " << argv[0] << " <host>" << endl;
return EXIT_FAILURE;
}
// 解析主机名或IP地址
hostent* host = gethostbyname(argv[1]);
if (host == NULL) {
cerr << "Error: Failed to resolve hostname." << endl;
return EXIT_FAILURE;
}
// 创建原始套接字
int sockfd = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);
if (sockfd < 0) {
cerr << "Error: Failed to create socket." << endl;
return EXIT_FAILURE;
}
// 设置套接字选项(禁止IP头部自动填充)
int optval = 1;
if (setsockopt(sockfd, IPPROTO_IP, IP_HDRINCL, &optval, sizeof(optval)) < 0) {
cerr << "Error: Failed to set socket option." << endl;
return EXIT_FAILURE;
}
// 设置超时时间
struct timeval timeout;
timeout.tv_sec = 1;
timeout.tv_usec = 0;
if (setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout)) < 0) {
cerr << "Error: Failed to set socket option." << endl;
return EXIT_FAILURE;
}
// 构造目标地址
sockaddr_in dest_addr;
memset(&dest_addr, 0, sizeof(dest_addr));
dest_addr.sin_family = AF_INET;
dest_addr.sin_addr = *(in_addr*)host->h_addr_list[0];
// 发送若干个ICMP请求报文
const int NUM_PACKETS = 4;
for (int seq = 1; seq <= NUM_PACKETS; seq++) {
timespec start_time;
clock_gettime(CLOCK_MONOTONIC, &start_time);
send_packet(sockfd, dest_addr, seq);
if (!receive_packet(sockfd, dest_addr, seq, start_time)) {
cout << "Request timed out." << endl;
}
sleep(1); // 暂停1秒钟
}
// 关闭套接字
close(sockfd);
return EXIT_SUCCESS;
}
```
这个程序利用了Linux下的原始套接字来发送和接收ICMP报文。在主函数中,首先解析了主机名或IP地址,然后创建了原始套接字并设置了一些套接字选项。接着,程序发送了若干个ICMP请求报文,并等待接收ICMP回复报文。每收到一个回复报文,程序就计算并显示延迟。如果超时,程序会输出"Request timed out."的提示。最后,程序关闭了套接字并退出。
阅读全文