Linux下ping代码的错误

本文介绍了一个自制的Ping工具实现细节,包括如何使用原始套接字发送和接收ICMP数据包,解析IP和ICMP头部信息,计算校验和,以及处理超时和错误情况。通过具体代码示例展示了整个过程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

#include <sys/socket.h>
#include <sys/time.h>
#include <sys/signal.h>
#include <netdb.h>
#include <netinet/ip.h>
#include <netinet/ip_icmp.h>
#include <netinet/in.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <ctype.h>
#include <errno.h>

 

#define IP_HEAD_LEN 20
#define ICMP_LEN 8
#define BUFFER_SIZE 50 * 1024


int ip_fd;
int p_id;
int packet_len;
struct sockaddr_in send_addr;
char send_buf[1024];
int sequence;
struct hostent *host;
int flag;


int main(int argc, char **argv)
{
 if(argc != 2){
          fprintf(stderr, "usage: ping <host|ip_address>./n");
          exit(1);
     }
 ip_fd = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);
 if(ip_fd < 0){
  fprintf(stderr, "raw socket error./n");
          exit(1);
     }
     setuid(getpid());
     ping(argv[1]);
}

void handle_alarm(int signo)

 send_ip();
 alarm(1);
}

ping(char *argv)
{
 struct sigaction act;
 act.sa_handler = handle_alarm;
 act.sa_flags = 0;
     sigemptyset(&act.sa_mask);
     sigaction(SIGALRM, &act, NULL);
     p_id = getpid();
 /*只须填写地址信息,不需要指定端口信息,因为原始套接字在传输层之下*/
     send_addr.sin_family = AF_INET;
 /*判断是主机名还是ip地址*/
     if(inet_addr(argv) == INADDR_NONE){
        /*是主机名*/
   if((host = gethostbyname(argv)) == NULL){
        /*主机名错误*/
   perror("get host by name error: unknow host.");
              exit(1);
         }
 memcpy(&send_addr.sin_addr, host->h_addr, host->h_length);
 }
 else{
        /*是ip地址*/
 inet_aton(argv, &send_addr.sin_addr);
 }
 printf("ping %s(%s) %d(%d) bytes of data/n", argv,inet_ntoa(send_addr.sin_addr), sizeof(struct timeval),sizeof(struct     timeval) + IP_HEAD_LEN + ICMP_LEN);
 flag = 0;
 /*触发一个SIGALRM信号*/
   raise(SIGALRM);
 recv_ip();
}

unsigned short ip_checksum(unsigned short *pcheck, int check_len)
{
 int nleft = check_len;
 int sum = 0;
     unsigned short *p = pcheck;
     unsigned short result = 0;
 while(nleft > 1){
  sum = sum + *p ++;
  nleft -= 2;
     }
 if(nleft == 1){
  *(unsigned char *)(&result) = *(unsigned char *)p;
  sum += result;
     }
 sum = (sum >> 16) + (sum & 0xFFFF);
     sum += (sum >> 16);
 result = sum;
 return result;
}

int sequence = 0;
int packet_len = IP_HEAD_LEN + ICMP_LEN;
send_ip(void)
{
 if(sequence != 0 && !flag){
  printf("Destination Host Unreachable/n");
 }
 int len;
 struct icmphdr *icmp_p;
 icmp_p = (struct icmphdr *)send_buf;
 /*填写ICMP报文类型*/
     icmp_p->type = ICMP_ECHO;
     /*填写ICMP报文的代码*/
     icmp_p->code = 0;
     /*填写ICMP报文的标识符*/
     (icmp_p->un).echo.id = p_id;
     /*填写ICMP报文的序号,并增加ICMP的序号*/   
     (icmp_p->un).echo.sequence = sequence;
 /*记录发送时间*/
     gettimeofday((struct timeval*)(icmp_p + 1), NULL);
 /*报文的长度等于IP包长度加上ICMP报文长度和数据长度*/
 len = sizeof(struct timeval) + packet_len;
 /*计算ICMP的校验和*/
     icmp_p->checksum = 0;
     icmp_p->checksum = ip_checksum((u_short *)icmp_p, len);
        /*发送ICMP数据包*/
     if(sendto(ip_fd, send_buf, len, 0, (struct sockaddr*)&send_addr, sizeof(send_addr)) < 0){
  fprintf(stderr, "send to error./n");
 }
}


recv_ip(void)
{
 char recv_buf[1024];
 int len;
     int n;
 struct ip *ip_p;
     struct timeval *time_now, *time_send;
     struct timeval now;
 int iphead_len;
     int icmp_len;
 struct icmphdr *icmp_p;
 float delay;
 while(1){
  n = recvfrom(ip_fd,recv_buf,sizeof(recv_buf),0,NULL,NULL);
  printf("%d,flag:%d,sequence:%d/n",n,flag,sequence);
  if(n < 0){
   if(errno = EINTR)
    continue;
   else{
    printf("recvfrom error./n");
    continue;
              }
         }
  ip_p = (struct ip*)recv_buf;
         /*获取IP包头长度*/
  iphead_len = ip_p->ip_hl<<2;
         /*获取IP数据包中包含的ICMP报文*/
          icmp_p = (struct icmphdr *)(recv_buf + iphead_len);
         /*计算ICMP报文长度,它等于接受到的长度减去IP包头的长度*/
          icmp_len = n - iphead_len;
  if(icmp_len < 8){
               fprintf(stderr, "error icmp len = %d./n", icmp_len);
         }
         /*如果ICMP类型相同则输出显示*/
  if(icmp_p->type == ICMP_ECHOREPLY){
   if((icmp_p->un).echo.id != p_id)
               return;
               if(icmp_len < 16)
                   printf("icmplen = %d./n", icmp_len);
   flag = 1;//表示已经接收到回文;
   sequence++;
   gettimeofday(&now, NULL);
   time_now = &now;
   time_send = (struct timeval*)(icmp_p + 1);
   if((time_now->tv_usec -= time_send->tv_usec) < 0){
    time_now->tv_sec --;
                time_now->tv_usec += 1000000;
                 }
   time_now->tv_sec -= time_send->tv_sec;
   /*计算延时*/
   delay = time_now->tv_sec * 1000.0 + time_now->tv_usec / 1000.0;
   /*打印接收到的报文相关信息*/
   printf("%d(%d) bytes from %s: icmp_seq=%d ttl=%d time=%.3fms/n",
               icmp_len, n, inet_ntoa(send_addr.sin_addr), (icmp_p->un).echo.sequence,
               ip_p->ip_ttl, delay);
  }
 }
}

 

那位高手帮忙看看这段代码有什么问题?怎么总出现下面的情况?

10.129.73.25是本机的网关;
[root@localhost6 123]# ./ping 10.129.73.25
ping 10.129.73.25(10.129.73.25) 8(36) bytes of data
-1,flag:0,sequence:0
-1,flag:0,sequence:0
-1,flag:0,sequence:0
-1,flag:0,sequence:0
-1,flag:0,sequence:0
-1,flag:0,sequence:0


10.129.73.100是本机的IP
[root@localhost6 123]# ./ping 10.129.73.100
ping 10.129.73.100(10.129.73.100) 8(36) bytes of data
56,flag:0,sequence:0
56,flag:0,sequence:0
36(56) bytes from 10.129.73.100: icmp_seq=0 ttl=64 time=0.308ms
-1,flag:1,sequence:1
56,flag:1,sequence:1
56,flag:1,sequence:1
36(56) bytes from 10.129.73.100: icmp_seq=1 ttl=64 time=0.213ms
-1,flag:1,sequence:2
56,flag:1,sequence:2
56,flag:1,sequence:2
36(56) bytes from 10.129.73.100: icmp_seq=2 ttl=64 time=0.163ms
-1,flag:1,sequence:3
56,flag:1,sequence:3
56,flag:1,sequence:3
36(56) bytes from 10.129.73.100: icmp_seq=3 ttl=64 time=0.147ms
-1,flag:1,sequence:4
56,flag:1,sequence:4
56,flag:1,sequence:4
36(56) bytes from 10.129.73.100: icmp_seq=4 ttl=64 time=0.156ms
-1,flag:1,sequence:5
56,flag:1,sequence:5
56,flag:1,sequence:5
36(56) bytes from 10.129.73.100: icmp_seq=5 ttl=64 time=0.146ms
-1,flag:1,sequence:6
56,flag:1,sequence:6
56,flag:1,sequence:6
36(56) bytes from 10.129.73.100: icmp_seq=6 ttl=64 time=0.145ms

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值