c语言 数据结构c语言版 第100行哪里出错了

专业文档是百度文库认证用户/机構上传的专业性文档文库VIP用户或购买专业文档下载特权礼包的其他会员用户可用专业文档下载特权免费下载专业文档。只要带有以下“專业文档”标识的文档便是该类文档

VIP免费文档是特定的一类共享文档,会员用户可以免费随意获取非会员用户需要消耗下载券/积分获取。只要带有以下“VIP免费文档”标识的文档便是该类文档

VIP专享8折文档是特定的一类付费文档,会员用户可以通过设定价的8折获取非会員用户需要原价获取。只要带有以下“VIP专享8折优惠”标识的文档便是该类文档

付费文档是百度文库认证用户/机构上传的专业性文档,需偠文库用户支付人民币获取具体价格由上传人自由设定。只要带有以下“付费文档”标识的文档便是该类文档

共享文档是百度文库用戶免费上传的可与其他用户免费共享的文档,具体共享方式由上传人自由设定只要带有以下“共享文档”标识的文档便是该类文档。

版权声明:本文为博主原创文章转载需注明出处。 /qq_/article/details/

前几天脑子里忽然蹦出来一个想法想试着用C语言写一个自己的Ping命令出来。开始还有些茫然无措因为此前没有接触過网络编程方面的知识。幸运的是对Ping的实现原理还是比较清楚的难度和挑战皆在于网络编程方面的零基础,再加上现在的课程比较多所以只能每天抽出一部分时间来学习所需的网络编程知识。大概会持续个两三周既然如此,索性开个博客记录下整个学习的过程,也算是每天的总结了吧

这也算是一个小项目了,当然不能动手就开始敲代码总得把思路先理清楚。搞清楚做什么?怎么做然后才是實现。

做什么当然是Ping命令了,来看看这家伙的真实面貌:

额额这个目标仿佛是有些空洞的,等于没说应该先弄清楚Ping的实现原理才对。于是各种回忆翻课本,上网查资料总结如下。

(ICMP回声应答)它是用来检查网络是否通畅或者网络连接速度的命令。它所利用的原悝是这样的:利用网络上机器IP地址的唯一性给目标IP地址发送一个数据包,再要求对方返回一个同样大小的数据包来确定两台网络机器是否连接相通时延是多少。

其中关键在于发送ICMP数据包然后对接收到的包进行一定的处理。不可避免我们要发送ICMP包,必须自己来构建一個包出来再来回顾一下ICMP:

使用宏定义令表达更简洁,其中ICMP报头为8字节,数据报长度最大为64K字节。

  1. 校验和算法:这一算法称为网际校验和算法把被校验的数据16位进行累加,然后取反码若数据字节长度为奇数,则数据尾部补一个字节的0以凑成偶数此算法适用于IPv4、ICMPv4、IGMPV4、ICMPv6、UDP和TCP校验和,更详细的信息请参考RFC1071校验和字段为上述ICMP数据结构c语言版的icmp_cksum变量。
  2. 标识符:用于唯一标识ICMP报文, 为上述ICMP数据结构c语言版的icmp_id宏所指的变量
  3. 顺序号:ping命令的icmp_seq便由这里读出,代表ICMP报文的发送顺序为上述ICMP数据结构c语言版的icmp_seq宏所指的变量。

ICMP封装好后是这样的:


ICMP是为网關和目标主机而提供的一种差错控制机制使它们在遇到差错时能把错误报告给报文源发方。ICMP协议是IP层的一个协议但是由于差错报告在發送给报文源发方时可能也要经过若干子网,因此牵涉到路由选择等问题所以ICMP报文需通过IP协议来发送。ICMP数据报的数据发送前需要两级封裝:首先添加ICMP报头形成ICMP报文再添加IP报头形成IP数据报。因此我们还需知道IP报文的格式

整个ICMP报文作为了IP报文的数据部分,再给它加上IP报头僦成了这样:

其中ping程序只使用以下数据:

  • 生存时间TTL(Time To Live)以秒为单位指出IP数据报能在网络上停留的最长时间,其值由发送方设定并在经過路由的每一个节点时减一,当该值为0时数据报将被丢弃,是上述IP数据结构c语言版的ip_ttl变量

第一天将ping的实现原理和有关报文的格式理清楚。

基础该该复习的也都复习了所以今天图书馆挑了几本书,先来大体了解一下关于套接字里边的东西

  • 《LInux高性能服务器编程》
  • 《Unix网絡编程 第2卷》
  • 《Unix环境高级编程》

这些书都不薄,想在一天从零基础内看完一本都是不可能的我只是打算通读一下其中关于套接字和网络編程基础的章节,以期有对这部分知识一个大体了解而非精读。这样的话加起来也就100多页的样子今天下来,也算初步有个认识了后期用到具体的函数时,再具体深入的了解

现在可以先初步搭一个框架出来:

  1. 获取(转换)目标IP地址
/* 将点分十进制ip地址转换为网络字节序 */ /* 轉换失败,表明是主机名,需通过主机名获取ip */

为什么要将点分十进制ip地址转换为网络字节序(TCP/IP协议栈使用大端字节序:详细介绍:)

通常,然人们习惯用可读性好的字符串来表示IP地址比如用点分十进制字符串表示IPv4地址,以及以十六进制字符串表示IPV6地址但编程中我们需要艏先把它们转化为整数(二进制数)方能使用。而记录日志时则相反我们要把整数表示的IP地址转化为可读的字符串。下面3个函数可用与點分十进制字符串表示的IPV4地址和用网络字节序表示的IPv4地址之间进行转化:

最后注释掉的三个函数尚未实现

要实现发送报文的函数,首先嘚有报文才行所以要先实现一个设置ICMP报文的函数。有关ICMP报文的信息已经了解的差不多了就看具体实现了:

如上,单独将计算校验和的項给分离了出来为了逻辑更加清晰。设置一个发送缓冲区SEND_BUFFER_SIZE为暂为128(字节),理论上来讲64字节足够了,后边视情况再修改计算校验和的方法很简单,网上示例很多知道原理,写出来不是什么难事

Ping命令中需要显示的信息,包括icmp_seq和ttl都已有实现的办法但是还不知道往返时間rtt。为了实现这一功能可以通过ICMP数据报携带一个时间戳来实现。(在回送请求报文中包含了的可选数据在应答报文中包含了该可选数據的一个副本)

*tzp);来获取系统当前时间。其中tv_sec为秒数tv_usec为微妙数。在发送报文和接收报文时各通过gettimeofday函数获取一次时间两次时间差就可以求出往返时间。我把这个时间戳作为了数据信息tzp指针表示时区,这里只是要时间差所以不需要,赋为NULL值

既然设置好了ICMP报文,便可以發送了

但是发送往哪发送呢?当然是往套接字上发送了可以把套接字看成一个文件,往这上边写数据即代表发送。 Unix/Linux的一个哲学是:┅切皆文件socket也不例外,它就是可读/可写/可控制/可关闭的文件描述符下面的socket调用可创建一个socket:

domain参数告诉系统使用哪个底层协议族。对于TCP/IP協议族而言该参数应该设置为PF_INET(用于IPv4)或PF_INET6(用于IPv6);type参数指定服务类型,进一步确定通信特征这里选择SOCK_RAW,表示IP协议的数据报接口参数protocol通常为0,表示为给指定的域和套接字选择默认协议这里直接选择ICMP协议。表示ICMP协议的编号为IPROTO_ICMP这里可以直接用它,或者通过一下这种方式获取:

getprotobyname()昰一个函数返回对应于给定协议名的相关协议信息。此函数可以将协议名字映射为协议编号

sendto往sockfd上写入数据,buf和len参数分别指定写缓冲区嘚位置和大小dest_addr参数指定接收端的socket地址,addrlen参数1则指定该地址的长度flag参数为数据收发提供了额外控制,通常为0.send成功时返回实际写入的数据嘚长度失败则返回-1并设置errno。

dest_addr类型为专用socket地址所有专用socket地址类型的变量在实际使用是都需要转化为通用socket地址类型sockaddr(强制转换即可),因為所有scket编程接口使用的地址参数的类型都是sockaddr

好不容易写出这几个函数,但是目前还有点小问题后边继续修改。先不管这些

recvfrom读取sockfd上的數据,buf和len参数分别指定度缓冲区的位置和大小ICMP数据报是封装IP报文里发送的,IP协议是无连接的不可靠的协议。所以每次读取数据都要获取发送端的socket地址即参数src_addr所指的内容,addlen参数则指定该地址的长度返回值:返回接收数据的字节长度;若无可用数据或对等方已经按序结束,返回0;若出错返回-1.

当调用recvfrom时,需要设置addlen参数指向一个整数该整数包含addr所指向的套接字缓冲区的字节长度。返回时该整数设为该哋址的实际字节长度。

GetRtt用来获取两个时间戳的差结果保存在第一个参数所指空间处。

其实这不是真正的第七天了只是有些时候比较忙,没来得及写博客到昨天为止,整个框架已经一目了然了无非是一些细节上的修改以及最后的统计信息。

在下边要贴上的源码基本就昰最终版了和前面的细节处有些许不同也是正常,是因为我做了修改为了统计信息方便。比如统计信息中的avgmin,maxmdev几个时间的计算,昰我一开始没有考虑到的只能后面修改。至于前面已经写好的博文也没必要改了如果还有后续的修改,我会更新在博客最后的源程序Φ也会上传到github,文章末尾会附上本程序在github中的链接

总共的源文件有三个,分别是:main.c, ping.c, ping.h,头文件和公用的接口都放在ping.h中函数的实现在ping.cΦ,main.c中负责调用已有的接口搭建程序的骨架,将这些函数拼接起来

//判断接收到的报文是否是自己所发报文的响应
/* 将点分十进制ip地址转換为网络字节序 */ /* 转换失败,表明是主机名,需通过主机名获取ip */

统计信息中mdev的计算方法

在运行 ping 命令的时候里面有一项输出叫 mdev:

它是什么意思呢? ping 的手册中并没有提到我们不妨看一下 ,见 ping_common.c:

也就是这个平均偏差的公式:

所以 mdev 就是 Mean Deviation 的缩写它表示这些 ICMP 包的 RTT 偏离平均值的程度,这個值越大说明你的网速越不稳定

因为创建这个套接字需要有root权限,所以运行的时候只能这样了(不过我还会继续优化)

我要回帖

更多关于 数据结构c语言版 的文章

 

随机推荐