代码 C++ ping
时间: 2023-09-28 19:12:10 浏览: 174
ping命令的C++代码
4星 · 用户满意度95%
Ping 是一种基于 ICMP 协议的网络诊断工具,使用 C++ 编写的 ping 程序需要使用原始套接字(raw socket),并且需要使用 root 权限才能运行。下面是一个简单的 ping 程序实现,可以发送 ICMP echo 请求并接收 ICMP echo 响应:
```c++
#include <iostream>
#include <cstdlib>
#include <cstring>
#include <ctime>
#include <cmath>
#include <unistd.h>
#include <netinet/ip_icmp.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <arpa/inet.h>
using namespace std;
const int PACKET_SIZE = 4096;
const int MAX_HOPS = 64;
// 计算校验和
unsigned short checksum(unsigned short *buf, int len) {
unsigned long sum = 0;
while (len > 1) {
sum += *buf++;
len -= 2;
}
if (len == 1) {
sum += *(unsigned char *) buf;
}
sum = (sum >> 16) + (sum & 0xFFFF);
sum += (sum >> 16);
return (unsigned short) ~sum;
}
// 发送 ICMP echo 请求
bool send_icmp_request(int sockfd, const sockaddr_in &addr, int seq) {
timeval tv{};
gettimeofday(&tv, nullptr);
icmp icmp_request{};
icmp_request.icmp_type = ICMP_ECHO;
icmp_request.icmp_code = 0;
icmp_request.icmp_id = getpid() & 0xFFFF;
icmp_request.icmp_seq = seq;
icmp_request.icmp_cksum = 0;
icmp_request.icmp_timestamp = tv.tv_sec * 1000 + tv.tv_usec / 1000;
icmp_request.icmp_cksum = checksum((unsigned short *) &icmp_request, sizeof(icmp_request));
int n = sendto(sockfd, &icmp_request, sizeof(icmp_request), 0, (sockaddr *) &addr, sizeof(addr));
if (n == -1) {
cerr << "Failed to send ICMP request: " << strerror(errno) << endl;
return false;
}
return true;
}
// 接收 ICMP echo 响应
bool recv_icmp_reply(int sockfd, int seq, sockaddr_in &addr, double &rtt) {
char buf[PACKET_SIZE]{};
sockaddr_in from{};
socklen_t from_len = sizeof(from);
timeval tv{};
fd_set readfds{};
FD_ZERO(&readfds);
FD_SET(sockfd, &readfds);
tv.tv_sec = 1;
tv.tv_usec = 0;
int n = select(sockfd + 1, &readfds, nullptr, nullptr, &tv);
if (n == -1) {
cerr << "Failed to select: " << strerror(errno) << endl;
return false;
} else if (n == 0) { // 超时
return false;
}
n = recvfrom(sockfd, buf, PACKET_SIZE, 0, (sockaddr *) &from, &from_len);
if (n == -1) {
cerr << "Failed to receive ICMP reply: " << strerror(errno) << endl;
return false;
}
timeval now{};
gettimeofday(&now, nullptr);
iphdr *ip_hdr = reinterpret_cast<iphdr *>(buf);
unsigned int ip_hdr_len = ip_hdr->ihl * 4;
icmp *icmp_reply = reinterpret_cast<icmp *>(buf + ip_hdr_len);
if (icmp_reply->icmp_type != ICMP_ECHOREPLY || icmp_reply->icmp_id != (getpid() & 0xFFFF) ||
icmp_reply->icmp_seq != seq) {
return false;
}
rtt = (now.tv_sec - icmp_reply->icmp_timestamp / 1000) * 1000 +
(now.tv_usec - icmp_reply->icmp_timestamp % 1000 * 1000) / 1000.0;
addr = from;
return true;
}
// 打印 IP 地址
void print_ip_address(const sockaddr_in &addr) {
char ip_str[INET_ADDRSTRLEN];
inet_ntop(AF_INET, &addr.sin_addr, ip_str, INET_ADDRSTRLEN);
cout << ip_str;
}
// ping 操作
void ping(const char *host) {
int sockfd = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);
if (sockfd == -1) {
cerr << "Failed to create raw socket: " << strerror(errno) << endl;
return;
}
sockaddr_in dest_addr{};
memset(&dest_addr, 0, sizeof(dest_addr));
dest_addr.sin_family = AF_INET;
dest_addr.sin_addr.s_addr = inet_addr(host);
cout << "PING " << host << " (" << host << ") 56(84) bytes of data." << endl;
for (int ttl = 1; ttl <= MAX_HOPS; ++ttl) {
cout << ttl << " ";
// 设置 TTL
if (setsockopt(sockfd, IPPROTO_IP, IP_TTL, &ttl, sizeof(ttl)) == -1) {
cerr << "Failed to set TTL: " << strerror(errno) << endl;
break;
}
// 发送 3 个 ICMP echo 请求
double rtt_sum = 0;
int n_received = 0;
for (int i = 0; i < 3; ++i) {
if (send_icmp_request(sockfd, dest_addr, i + 1)) {
sockaddr_in from{};
double rtt = 0;
if (recv_icmp_reply(sockfd, i + 1, from, rtt)) {
print_ip_address(from);
cout << " time=" << rtt << " ms" << endl;
rtt_sum += rtt;
n_received++;
} else {
cout << " *" << endl;
}
} else {
cout << " *" << endl;
}
usleep(100000); // 等待 100 毫秒
}
if (n_received > 0) {
double avg_rtt = rtt_sum / n_received;
cout << " avg=" << avg_rtt << " ms" << endl;
}
if (dest_addr.sin_addr.s_addr == inet_addr("127.0.0.1")) {
break;
}
// 到达目标主机
if (n_received > 0) {
break;
}
}
close(sockfd);
}
int main(int argc, char *argv[]) {
if (argc != 2) {
cerr << "Usage: " << argv[0] << " <host>" << endl;
return -1;
}
ping(argv[1]);
return 0;
}
```
该程序使用 raw socket 发送 ICMP echo 请求,并接收 ICMP echo 响应。程序首先创建一个 raw socket,然后使用 setsockopt 函数设置 TTL(Time To Live,生存时间),从 1 开始循环发送 ICMP echo 请求,并接收 ICMP echo 响应。每发送一个 ICMP echo 请求,就等待 100 毫秒,然后尝试接收 3 次 ICMP echo 响应。如果接收到响应,就输出主机 IP 地址和往返时间(RTT,Round Trip Time);如果没有接收到响应,则输出星号。当 TTL 达到 64 或者到达目标主机时,程序退出。
使用该程序时,需要传入一个参数:目标主机的 IP 地址或域名。例如,如果要 ping 百度网站,可以运行以下命令:
```
sudo ./ping www.baidu.com
```
程序会输出每个 TTL 的主机 IP 地址和 RTT。如果程序没有输出任何信息,则说明目标主机不可达。
阅读全文