linux下ping程序实现

  • 内容
  • 评论
  • 相关

这里使用socket实现ping程序的所有功能。

可以借此了解以下icmp协议的设计细节和注意事项。

支持以下功能:

  • 支持ping域名和ip地址
  • 支持自定义发包个数
  • 支持自定义ttl
  • 支持超时设定

使用方法:

//打印帮助
Linux_ping -h

//参数设定
Linux_ping -c 10 -t 100 -w 100 ip/域名

来介绍下功能关键点:

创建ICMP套接字

//RAW套接字,需要root权限
g_sockfd=socket(AF_INET,SOCK_RAW,IPPROTO_ICMP);

TTL设定

TTL(Time To Live)

该字段指定IP包被路由器丢弃之前允许通过的最大网段数量

如果设置的ttl过小,那么就就会收不到回包

UINT32 ttl = TTL;
setsockopt(g_sockfd, IPPROTO_IP, IP_TTL, (char *)&ttl, sizeof(ttl)); 

域名到IP的转换

//是主机名
if (inet_addr(szHost) == INADDR_NONE)
{
        host = gethostbyname(szHost);
        if(host==NULL)
        {
            perror("获取主机名错误");
            return -1;
        }
        //成功的话,ip地址存在host里面
}

字符串到IP的转换

dest_addr.sin_addr.s_addr = inet_addr(szHost);
printf("IP:%s\n", inet_ntoa(g_dest_addr.sin_addr));

网络字节序

网络字节序是大端

X86体系是小端字节序,一般都需要自行转换字节序

以下常用函数用于字节序转换:

//主机序->网络序
htonl()
//网络序->主机序
ntohl()

自定义ICMP报头

//类型
icmp->icmp_type = ICMP_ECHO;
icmp->icmp_code = 0;
//校验码,必须默认置0
icmp->icmp_cksum = 0;
icmp->icmp_seq = pack_no;
icmp->icmp_id=pid;

//头部8个字节
packsize = 8 + g_datalen;
icmp->icmp_cksum=cal_chksum((unsigned short *)icmp, packsize);

使用alarm()设置超时

alarm()主要功能: 设置信号传送闹钟

即用来设置信号SIGALRM在经过参数seconds秒数后发送给目前的进程

果在seconds秒内再次调用了alarm函数则后面定时器的设置将覆盖前面的设置

主要逻辑:

signal(SIGALRM,Statistic);

while(g_nRecv < g_nNeedSend)
{
    alarm(g_nWait);
    recvfrom(g_sockfd, g_RecvPacket, sizeof(g_RecvPacket), 0, (struct sockaddr *)&g_from,&nfromlen);
}

这里使用了阻塞recvfrom(),如果接收超时会触发我们设置的signal函数

运行结果:
image

github:
https://github.com/daterlove/Linux-Ping