网工干货知识

超全学习笔记
当前位置:首页 > 干货知识

C语言中的“Ping”函数

更新时间:2026年03月27日   作者:spoto   标签(Tag):

先决条件/前提条件: ICMP | 原始套接字 | 互联网校验和 | DNSPing是调试互联网过程中必不可少的工具。它是一种基本的互联网工具,可以帮助用户验证某个IP地址是否存在,以及该地址是否能够接受请求。除此之外,Ping还具备其他一些功能。

Ping通过打开一个接口来发送ICMP数据包。RAW套接字它与TCP和UDP是分开的。由于IP本身并没有用于发送错误和控制信息的机制,因此,这一切都取决于其他协议来处理这些任务。互联网控制消息协议ICMP用于实现错误控制功能。它主要用于报告各种错误以及进行相关管理操作。

Ubuntu Ping的示例

Ping www.google.com  发送了3个数据包,接收了3个数据包,数据包传输成功,无丢失情况。总传输时间为3110毫秒。  来自172.217.194.105的响应数据长度为64字节:  icmp_seq=1, ttl=46, 时间=116毫秒;  icmp_seq=2, ttl=46, 时间=102毫秒;  icmp_seq=3, ttl=46, 时间=119毫秒。  ^C  --- www.google.com的ping统计信息 ---  总共发送了3个数据包,3个数据包都被成功接收,没有数据包丢失。总传输时间为3110毫秒。

工作机制/运作方式平的

Internet Ping程序的工作原理类似于声纳定位系统。它向指定的计算机发送一个包含ICMP ECHO_REQUEST信息的短数据包,而接收方则会返回一个ECHO_REPLY数据包作为回应。这个数据包中的TTL(生存时间)字段决定了数据包可以经过的最大路由次数。如果数据包无法到达目标计算机,那么发送方将会收到一个关于该问题的反馈信息。错误类型包括以下几种:

  • TTL在传输过程中已经过期了。
  • 目标主机无法访问。
  • 请求已超时,即没有收到回复。
  • 未知的主机

实施/执行一个简单的ping程序的执行步骤如下:

  • 请拿一下主机名作为输入
  • 做吧DNS查询

DNS查询可以通过以下方式来完成:gethostbyname()`gethostbyname()`函数可以将普通的、人类可读的网址转换为某种数据结构。主机名该字段包含以二进制点表示法表示的IP地址,同时还会标明地址的类型。

  • 有些ping程序,比如那些在Ubuntu系统中得到支持的那些程序。反向DNS查询反向DNS查询是通过以下方式进行的:getnameinfo()它可以将点分隔的IP地址转换为主机名。例如,对google.com进行ping测试时,通常会得到这样的奇怪地址:bom07s18-in-f14.1e100.net。这种现象是由于进行了反向DNS查询所导致的。
  • 打开一个原始套接字使用 SOCK_RAW 套接字,并将协议设置为 IPPROTO_ICMP。注意:使用原始套接字需要超级用户的权限,因此必须使用 sudo 命令来运行此代码。
  • 当按下 CTRL + C 时,ping 会发出一个报告。这个中断会被中断处理程序捕获,该处理程序会将我们的 ping 循环条件设置为 false。
  • 现在,我们进入主要的ping发送循环阶段了。我们需要做到以下几点:
    • 请设置/设定TTL选项通过将套接字中的TTL值设置为某个特定数值,可以限制数据包可以经过的跳数。
    • 请设置/设定超时如果未设置超时时间,那么 `recv` 函数将会一直等待下去,从而导致循环无法继续执行。
    • 填满它吧。ICMP数据包如下所示:
      • 将数据包头部类型设置为ICMP_ECHO。
      • 将 `id` 设置为进程的进程ID。
      • 随机填充消息部分的内容。
      • 计算校验和,并将其填入校验和字段中。
      • 发送数据包
      • 请等待它完成吧。已收到这里的主要问题是,所接收到的数据包并不能说明目的地设备已经正常工作了。只有当收到来自目的地的回显响应时,才能说明目的地设备是正常的。而回显响应的发送者,其实就是目的地的操作系统内核。这个这是所有类型和代码的列表。这里的一个问题是,如果一切正常的话,程序会显示“类型69”和“代码0”,而不是代表“echo回复”的“0”。

以下是用于执行简单ping操作的代码。

C
// 用于实现“ping”功能的C语言程序// 编译命令:gcc -o ping ping.c// 运行命令如下:sudo./ping <主机名>#include<stdio.h>#include<sys/types.h>#include<sys/socket.h>#include<netinet/in.h>#include<arpa/inet.h>#include<netdb.h>#include<unistd.h>#include<string.h>#include<stdlib.h>#include<netinet/ip_icmp.h>#include<time.h>#include<fcntl.h>#include<signal.h>// 定义数据包相关的常量#define PING_PKT_S 64// ping数据包的大小#define PORT_NO 0// 自动选择的端口号#define PING_SLEEP_RATE 1000000// 延迟时间调整的频率(以微秒为单位)定义 RECV_TIMEOUT 的值为 1// 接收数据包的超时时间(以秒为单位)// 定义Ping循环整数pingloop=1;// ping数据包的结构结构体ping_pkt{结构体icmphdrHDR;字符/字母消息/信息[PING_PKT_S-sizeof(结构体icmphdr)];};// 函数声明无符号的校验和(无效/无意义*b,整数长度);无效/无意义intHandler(整数假货/赝品);字符/字母*DNS查询(字符/字母*地址主机,结构体sockaddr_in*地址信息);字符/字母*反向DNS查询(字符/字母*ip_addr);无效/无意义发送 Ping 请求(整数ping_sockfd,结构体sockaddr_in*ping_addr,字符/字母*ping_dom,字符/字母*ping_ip,字符/字母*rev_host);// 计算校验和(遵循 RFC 1071 标准)无符号的校验和(无效/无意义*b,整数长度){无符号的*缓冲区=b;无符号的整数总和=0;无符号的结果;为了(总和=0;长度>1;长度-=2)总和+=*缓冲区++;if(长度==1)总和+=*(无符号的字符/字母*)缓冲区;总和=(总和>>16)+(总和&0xFFFF);总和+=(总和>>16);结果=~总和;返回结果/成果;}// 中断处理程序无效/无意义intHandler(整数假货/赝品){pingloop=0;}// 执行DNS查询字符/字母*DNS查询(字符/字母*地址主机,结构体sockaddr_in*地址匹配){printf("\n正在解析DNS信息……\n");结构体主机名*宿主实体;字符/字母*ip=(字符/字母*)malloc(NI_MAXHOST*sizeof(字符/字母));if((宿主实体=gethostbyname(地址主机))==空值){// 未找到与主机名对应的IP地址返回空值;}// 填充地址结构体的内容strcpy(ip,inet_ntoa(*(结构体in_addr*)宿主实体->h_addr));(*地址转换).sin_family=宿主实体->h_addrtype;(*地址转换).sin_port=htons(PORT_NO);(*地址匹配).sin_addr.s_addr=*(*)宿主实体->h_addr;返回ip;}// 完成主机名的反向查找字符/字母*反向DNS查询(字符/字母*ip_addr){结构体sockaddr_intemp_addr;socklen_t长度;字符/字母缓冲区[NI_MAXHOST],*ret_buf;temp_addr.sin_family=AF_INET;temp_addr.sin_addr.s_addr=inet_addr(ip_addr);长度=sizeof(结构体sockaddr_in);if(获取名称信息((结构体sockaddr*)&temp_addr,长度,缓冲区,sizeof(缓冲区),空值,0,NI_NAMEREQD)){printf(无法解析主机名的反向查找结果。\n");返回空值;}ret_buf=(字符/字母*)malloc((strlen(缓冲区)+1)*sizeof(字符/字母));strcpy(ret_buf,缓冲区);返回ret_buf;}// 发起一个ping请求无效/无意义发送 Ping 请求(整数ping_sockfd,结构体sockaddr_in*ping_addr,字符/字母*ping_dom,字符/字母*ping_ip,字符/字母*rev_host){整数ttl_val值=64,消息数量=0,i,地址长度,旗帜=1,收到的消息数量=0;字符/字母rbuffer[128];结构体ping_pktpckt;结构体sockaddr_inr_addr;结构体时间单位开始时间,时间结束,TFS,TFE;双数/两倍rtt_msec=0,总毫秒数=0;结构体时间值电视之外;电视之外.TVSEC=接收超时;电视之外.tv_usec=0;clock_gettime(单调的时钟声,&TFS);// 将套接字的IP选项设置为TTL值,该值为64。if(setsockopt(ping_sockfd,SOL_IP,IP_TTL,&ttl_val值,sizeof(ttl_val值))!=0){printf("\n将套接字选项设置为TTL失败!\n");返回;}否则{printf("\n套接字设置为TTL……\n");}// 设置接收操作的超时时间setsockopt(ping_sockfd,SOL_SOCKET,SO_RCVTIMEO,(常量字符/字母*)&电视之外,sizeof电视之外);// 通过无限循环来发送ICMP数据包当…的时候(pingloop){// 用于判断数据包是否已被发送的标志旗帜=1;// 填充数据包bzero(&pckt,sizeof(
              马上抢免费试听资格
意向课程:*必选
姓名:*必填
联系方式:*必填
QQ:
思博SPOTO在线咨询

相关资讯

即刻预约

免费试听-咨询课程-获取免费资料