linuxc实现ping命令
-
要在Linux环境下实现ping命令,可以使用C语言编写一个简单的程序。
首先,需要包含以下头文件:
“`c
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
“`然后,定义一些宏和全局变量:
“`c
#define PACKET_SIZE 4096
#define MAX_WAIT_TIME 5
#define MAX_NO_PACKETS 3int sockfd, datalen = 56;
char sendpacket[PACKET_SIZE];
char recvpacket[PACKET_SIZE];
int nsend = 0, nreceived = 0;struct sockaddr_in dest_addr;
struct timeval tvrecv;
“`接下来,编写一些辅助函数,包括校验和计算、发送ICMP报文、接收并解析ICMP报文等:
校验和计算函数:
“`c
unsigned short cal_chksum(unsigned short *addr, int len)
{
int nleft = len;
int sum = 0;
unsigned short *w = addr;
unsigned short answer = 0;while (nleft > 1)
{
sum += *w++;
nleft -= 2;
}if (nleft == 1)
{
*(unsigned char *)(&answer) = *(unsigned char *)w;
sum += answer;
}sum = (sum >> 16) + (sum & 0xffff);
sum += (sum >> 16);
answer = ~sum;
return answer;
}
“`发送ICMP报文函数:
“`c
void send_packet()
{
int packetsize;
memset(sendpacket, 0, sizeof(sendpacket));
struct icmp *icmp = (struct icmp *)sendpacket;
icmp->icmp_type = ICMP_ECHO;
icmp->icmp_code = 0;
icmp->icmp_cksum = 0;
icmp->icmp_seq = nsend++;
icmp->icmp_id = getpid();
memset(icmp->icmp_data, 0xa5, datalen);
gettimeofday((struct timeval *)icmp->icmp_data, NULL);
packetsize = 8 + datalen;
icmp->icmp_cksum = cal_chksum((unsigned short *)icmp, packetsize);
sendto(sockfd, sendpacket, packetsize, 0, (struct sockaddr *)&dest_addr, sizeof(dest_addr));
}
“`接收并解析ICMP报文函数:
“`c
void recv_packet()
{
int n;
socklen_t fromlen;
extern int errno;
signal(SIGALRM, recv_packet);
fromlen = sizeof(dest_addr);
alarm(MAX_WAIT_TIME);
while (1)
{
if ((n = recvfrom(sockfd, recvpacket, sizeof(recvpacket), 0, (struct sockaddr *)&dest_addr, &fromlen)) < 0) { if (errno == EINTR) continue; perror("recvfrom error"); return; } gettimeofday(&tvrecv, NULL); icmp_handler(); nreceived++; break; }}```解析ICMP报文函数:```cvoid icmp_handler(){ struct icmp *icmp = (struct icmp *)recvpacket; if (icmp->icmp_type == ICMP_ECHOREPLY && icmp->icmp_id == getpid())
printf(“%d bytes from %s icmp_seq=%u ttl=%d time=%.2f ms\n”,
datalen,
inet_ntoa(dest_addr.sin_addr),
icmp->icmp_seq,
icmp->icmp_ttl,
(double)(tvrecv.tv_usec – ((struct timeval *)icmp->icmp_data)->tv_usec) / 1000);
}
“`最后,编写主函数,并实现ping命令的主要逻辑:
“`c
int main(int argc, char *argv[])
{
struct hostent *host;
struct protoent *protocol;
unsigned long inaddr = 0l;
int waittime = MAX_WAIT_TIME;
int size = 50 * 1024;
if (argc < 2) { printf("usage:%s hostname/IP address\n", argv[0]); exit(1); } if ((protocol = getprotobyname("icmp")) == NULL) { perror("getprotobyname"); exit(1); } if ((sockfd = socket(AF_INET, SOCK_RAW, protocol->p_proto)) < 0) { perror("socket error"); exit(1); } setuid(getuid()); setsockopt(sockfd, SOL_SOCKET, SO_RCVBUF, &size, sizeof(size)); bzero(&dest_addr, sizeof(dest_addr)); dest_addr.sin_family = AF_INET; if (inaddr = inet_addr(argv[1]) == INADDR_NONE) { if ((host = gethostbyname(argv[1])) == NULL) { perror("gethostbyname error"); exit(1); } memcpy((char *)&dest_addr.sin_addr, host->h_addr, host->h_length);
}
else
{
dest_addr.sin_addr.s_addr = inet_addr(argv[1]);
}
pid_t pid = getpid();
printf(“PING %s(%s):%d bytes data in ICMP packets.\n”, argv[1], inet_ntoa(dest_addr.sin_addr), datalen);
send_packet();
recv_packet();
printf(“%s:received = %d, lost = %d (%d%% loss)\n”, argv[1], nreceived, nsend – nreceived, (nsend – nreceived) / nsend * 100);
close(sockfd);
return 0;
}
“`将上述代码保存为一个文件(例如ping.c),然后通过以下命令编译并运行程序:
“`
gcc -o ping ping.c
sudo ./ping
“`其中,`
`指定要ping的主机名或IP地址。程序将输出类似于ping命令的结果。 这就是在Linux环境下使用C语言实现ping命令的方法。
2年前 -
要在Linux环境下实现ping命令,可以使用C语言编写代码。以下是基本的步骤和代码示例。
步骤1: 包含所需的头文件
“`c
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
“`步骤2: 定义数据结构
“`c
struct ping_pkt {
struct icmphdr hdr;
char msg[64-sizeof(struct icmphdr)];
};
“`步骤3: 实现校验和计算函数
“`c
unsigned short checksum(void *b, int len) {
unsigned short *buf = (unsigned short *)b;
unsigned int sum = 0;
unsigned short result;for (sum = 0; len > 1; len -= 2)
sum += *buf++;
if (len == 1)
sum += *(unsigned char *)buf;
sum = (sum >> 16) + (sum & 0xFFFF);
sum += (sum >> 16);
result = ~sum;
return result;
}
“`步骤4: 实现ping函数
“`c
void ping(const char *hostname) {
int sockfd;
struct sockaddr_in addr;
struct hostent *host;
struct ping_pkt pkt;
struct timeval tv;
int ttl_val = 64, msg_count = 0;sockfd = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);
if (sockfd < 0) { printf("Error in socket creation.\n"); return; } setsockopt(sockfd, SOL_IP, IP_TTL, &ttl_val, sizeof(ttl_val)); setuid(getuid()); addr.sin_family = AF_INET; host = gethostbyname(hostname); if (!host) { printf("Couldn't resolve hostname.\n"); return; } printf("PING %s (%s):\n", hostname, inet_ntoa(*(struct in_addr *)host->h_addr));while (msg_count < 10) { bzero(&pkt, sizeof(pkt)); pkt.hdr.type = ICMP_ECHO; pkt.hdr.un.echo.id = getpid(); for (int i = 0; i < sizeof(pkt.msg) - 1; i++) pkt.msg[i] = i + '0'; pkt.msg[sizeof(pkt.msg) - 1] = 0; pkt.hdr.un.echo.sequence = msg_count++; pkt.hdr.checksum = checksum(&pkt, sizeof(pkt)); usleep(200000); // 等待200ms gettimeofday(&tv, NULL); double sent_time = tv.tv_sec + tv.tv_usec / 1000000.0; if (sendto(sockfd, &pkt, sizeof(pkt), 0, (struct sockaddr *)&addr, sizeof(addr)) <= 0) { printf("Error in sending packet.\n"); continue; } struct ping_pkt rcv_pkt; socklen_t addr_len = sizeof(addr); if (recvfrom(sockfd, &rcv_pkt, sizeof(rcv_pkt), 0, (struct sockaddr *)&addr, &addr_len) <= 0) printf("Packet lost.\n"); else { gettimeofday(&tv, NULL); double rcv_time = tv.tv_sec + tv.tv_usec / 1000000.0; double rtt = rcv_time - sent_time; printf("Reply from %s: seq=%d, rtt=%.3f ms\n", inet_ntoa(addr.sin_addr), rcv_pkt.hdr.un.echo.sequence, rtt * 1000); } } close(sockfd);}```步骤5: 调用ping函数```cint main() { const char *hostname = "www.example.com"; ping(hostname); return 0;}```以上就是使用C语言在Linux环境下实现ping命令的基本步骤和代码示例。通过使用原始套接字和ICMP协议,可以发送和接收ping消息,并计算往返时间(RTT)以检测主机是否可达。
2年前 -
在Linux中,我们可以使用C语言来实现类似于ping命令的功能,即发送ICMP Echo请求并接收ICMP Echo回复。下面是一个基本的实现过程:
1. 导入必要的头文件
“`
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
“`2. 定义常量
“`
#define PACKET_SIZE 4096
#define MAX_WAIT_TIME 5
#define MAX_NO_PACKETS 3
“`
– PACKET_SIZE定义了发送和接收缓冲区的大小。
– MAX_WAIT_TIME定义了等待响应的最大时间(以秒为单位)。
– MAX_NO_PACKETS定义了发送ICMP Echo请求的次数。3. 定义全局变量
“`
struct timeval tvsend;
struct timeval tvrecv;char sendpacket[PACKET_SIZE];
char recvpacket[PACKET_SIZE];int sockfd;
int datalen = 56;
int nsend = 0, nreceived = 0;
pid_t pid;
“`
– tvsend和tvrecv用于记录发送和接收时间。
– sendpacket和recvpacket分别用于存储发送和接收数据。
– sockfd是套接字描述符。
– datalen定义了要发送的数据长度。
– nsend和nreceived分别表示已发送和已接收的ICMP Echo请求的数量。
– pid保存了进程的ID。4. 定义函数
– 定义校验和函数
“`
unsigned short cal_chksum(unsigned short *addr, int len) {
int nleft = len;
int sum = 0;
unsigned short *w = addr;
unsigned short answer = 0;while (nleft > 1) {
sum += *w++;
nleft -= 2;
}
if (nleft == 1) {
*(unsigned char *)(&answer) = *(unsigned char *)w;
sum += answer;
}
sum = (sum >> 16) + (sum & 0xffff);
sum += (sum >> 16);
answer = ~sum;
return answer;
}
“`
– 发送ICMP Echo请求函数
“`
void send_packet() {
struct timeval tv;
struct icmp *icmp;
int len;
icmp = (struct icmp *)sendpacket;
memset(icmp, 0, sizeof(struct icmp));
icmp->icmp_type = ICMP_ECHO;
icmp->icmp_code = 0;
icmp->icmp_cksum = 0;
icmp->icmp_seq = nsend++;
icmp->icmp_id = pid;
len = 8 + datalen;
icmp->icmp_cksum = cal_chksum((unsigned short *)icmp, len);
gettimeofday(&tv, NULL);
memcpy(&tvsend, &tv, sizeof(struct timeval));
sendto(sockfd, sendpacket, len, 0, (struct sockaddr *)&dest_addr, sizeof(dest_addr));
}
“`
– 接收ICMP Echo回复函数
“`
void recv_packet() {
int n, fromlen;
extern int errno;
struct timeval tv;
fd_set rfds;fromlen = sizeof(from);
gettimeofday(&tv, NULL);
tv.tv_sec = MAX_WAIT_TIME;
tv.tv_usec = 0;
FD_ZERO(&rfds);
FD_SET(sockfd, &rfds);while (1) {
int retval = select(sockfd + 1, &rfds, NULL, NULL, &tv);
if (retval == -1) {
if (errno == EINTR) {
continue;
}
else {
perror(“select”);
break;
}
}
else if (retval == 0) {
printf(“Request timeout for icmp_seq %d\n”, nsend);
break;
}
n = recvfrom(sockfd, recvpacket, sizeof(recvpacket), 0, (struct sockaddr *)&from, &fromlen);
if (n > 0) {
gettimeofday(&tv, NULL);
memcpy(&tvrecv, &tv, sizeof(struct timeval));
if (n < sizeof(struct ip)) { printf("Packet too short (%d bytes) from %s\n", n, inet_ntoa(from.sin_addr)); continue; } struct ip *ip = (struct ip *)recvpacket; struct icmp *icmp = (struct icmp *)(recvpacket + (ip->ip_hl << 2)); if (icmp->icmp_type == ICMP_ECHOREPLY && icmp->icmp_id == pid) {
printf(“%d bytes from %s: icmp_seq=%u ttl=%d time=%.3f ms\n”,
n, inet_ntoa(from.sin_addr), icmp->icmp_seq, ip->ip_ttl,
(tvrecv.tv_sec – tvsend.tv_sec) * 1000.0 + (tvrecv.tv_usec – tvsend.tv_usec) / 1000.0);
}
else {
printf(“Error: packet received is not a reply from %s\n”, inet_ntoa(from.sin_addr));
}
}
}
}
“`
– 结束处理函数
“`
void statistics() {
printf(“— %s ping statistics —\n”, dest_str);
printf(“%d packets transmitted, %d received, %d%% packet loss\n”,
nsend, nreceived, (nsend – nreceived) / nsend * 100);
close(sockfd);
}
“`5. 主函数
“`
int main(int argc, char *argv[]) {
struct hostent *host;
struct protoent *protocol;
unsigned long inaddr = 0l;if (argc < 2) { printf("usage: %s hostname/IP address\n", argv[0]); exit(1); } protocol = getprotobyname("icmp"); if (protocol == NULL) { perror("getprotobyname"); exit(1); } sockfd = socket(AF_INET, SOCK_RAW, protocol->p_proto);
if (sockfd < 0) { perror("socket"); exit(1); } setuid(getuid()); pid = getpid(); bzero(&dest_addr, sizeof(dest_addr)); dest_addr.sin_family = AF_INET; inaddr = inet_addr(argv[1]); if (inaddr == INADDR_NONE) { host = gethostbyname(argv[1]); if (host == NULL) { perror("gethostbyname"); exit(1); } memcpy((char *)&dest_addr.sin_addr, host->h_addr, host->h_length);
}
else {
memcpy((char *)&dest_addr.sin_addr, &inaddr, sizeof(inaddr));
}strcpy(dest_str, argv[1]);
printf(“PING %s (%s): %d data bytes\n”, dest_str, inet_ntoa(dest_addr.sin_addr), datalen);
send_packet();
recv_packet();
statistics();return 0;
}
“`以上是一个简单的实现ping命令的方法,可以基于这个基础上进行编写和扩展。需要注意的是,该实现使用了原始套接字,因此需要root权限才能运行。
2年前