ping程序的设计及实现报告_第1页
ping程序的设计及实现报告_第2页
ping程序的设计及实现报告_第3页
ping程序的设计及实现报告_第4页
ping程序的设计及实现报告_第5页
已阅读5页,还剩14页未读, 继续免费阅读

付费下载

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

..XX学院课程设计报告课程名称:计算机网络课程设计设计题目:ping程序的设计与实现系别:计算机与信息工程学院专业:计算科学与技术组别:第五小组起止日期:20XX12月1日~20XX12月8日指导教师:计算机科学与技术系二○一一年制课程设计题目Ping程序的设计与实现组长学号2011220125班级计专〔2班系别计算机与信息工程学院专业计算机科学与技术组员指导教师课程设计目的通过设计Ping程序,理解Ping程序的实现原理,并初步讲解了c语言网络编程技术。本章涉及很多网络编程函数和编程技巧,包括库文件的导入;winsock的初始化、注销;socket的创建、关闭;设置socket选项;根据主机名获取IP地址;从堆中分配一定数量的空间、释放从堆中分配的空间;数据报的发送;数据报的接等。课程设计所需环境WindowsXP+VisualC++6.0课程设计任务要求实现ping的基本功能,实现ping-t课程设计工作进度计划序号起止日期工作内容分工情况012011-12-1~2011-12-2展开思路讨论工作并搜集相关资料022011-12-3~2011-12-6具体制作,编写相关代码,制作相关窗口并实现,美化界面。032011-12-7~2011-12-8编写并完成课程设计报告指导教师签字:年月日教研室审核意见:教研室主任签字:年月日课程设计任务书一.Ping程序运行原理在网络层,除了IP协议之外,还有一些控制协议,如ICMP,ARP,DHCP等。ping的基础知识原始套接字原始套接字是允许访问底层传输协议的一种套接字类型。使用原始套接字操作IP数据报,可以进行路由跟踪,Ping等。另外,使用原始套接字需要知道许多下层协议结构的知识,所以下面讨论ICMP,IP,UDP,TCP格式。原始套接字有两种类型,第一种类型是在IP头种使用预定义的协议,如ICMP;第二种类型是在IP头种使用自定义的协议。下面使用创建原始套接字的方法。创建套接字的函数是socket<>或者WSASocket<>,只不过要将套接字类型指定为SOCK_RAW,代码如下:SOCKETsraw=::socket<AF_INET,SOCK_RAW,IPPROTO_ICMP>;创建原始套接字时socket函数的第三个参数protocol值将成为IP头中得协议域的值。IPPROTO_ICMP指定要使用ICMP。原始套解释提供管理下层传输的能力。他们可能会被恶意利用,因此,仅Administrator组的成员能够创建SOCK_RAW类型的套接字。任何人在WindowsNT下都可以创建原始套接字,但是没有Administrator权限的人不能用它来做任何事情,因为bind函数将会失败,出错码WSAEACCES..在上面的套接字创建代码种,我们使用ICMP,也可以使用IIGMP,UDP,IP或者原始IP,对应的宏定义分别是IPPROTO_IGMP,IPROTO_UDP,IPPROTO_IP或者IPPROTO_RAW。其中协议标志IPPROTO_UDP,IPPROTO_IP,和IPPROTO_RAW需要启动IP_HDRINCL选项。使用恰当的协议标志创建原始套接字之后,便可以在发送和接受调用种使用此套接字句柄了。无论IP_HDRINCL选项是否设置,在原始套接字上接收到的数据种都会将包含IP头。ICMP协议与校验和的计算互联网上得操作由路由器紧紧地监控着。当有异常饭送时候,具体事件通过ICMP报道,如目的不可到达,TTL超时等。这个协议也用来测试互联网。每个ICMP消息都封装在IP封包中,所以使用IP寻址,ICMP消息的格式如下:首8位表示ICMP的类型,通常可以分为请求消息和错误报告消息两类。接下来的八位表示ICMP代码,这个域进一步定义了请求或者是消息的类型。接下来八位表示icmp的校验和。它提供了ICMP头和他的实际数据。3.校验和的计算发送ICMP报文时,必须由程序自己计算校验和,并将它填入ICMP头部的对应域中。校验和的计算方法是:将数据以资为单位累加到一个双字中,如果数据长度为奇数,最后一个字节将被扩展到字,累加的结果是一个双字,最后将这个双字的高16bit和低16bit相加后取反,便得到了校验和。u_shortchecksum<u_short*buffer,intlen>{registerintnleft=len;registeru_short*w=buffer;registeru_shortanswer;registerintsum=0;//使用32位累加器,进行16位的反馈计算while<nleft>1>{sum+=*w++;nleft-=2;}//补全奇数位if<nleft==1>{u_shortu=0;*<u_char*><&u>=*<u_char*>w;sum+=u;}//将反馈的16位从高位移到低位sum=<sum>>16>+<sum&0xffff>;sum+=<sum>>16>;answer=~sum;return<answer>;}3.Ping程序设计思路:要实现ping程序,需要实现以下步骤:创建协议类型为IPPROTO_ICMP的原始套接字,设置套接字属性。创建并初始化ICMP封包。调用sendto函数向远程主机发送ICMP请求。调用recfrom函数接受ICMP响应。初始化ICMP头时先初始化消息类型和代码域,之后是回显请求头。程序首先定义了ICMP头的数据结构IMCP_HDR.。ICMP_HDR的定义如下:typedefstruct_ICMPHeader{u_charType;//类型u_charCode;//代码u_shortChecksum;//首部校验和u_shortID;//标识u_shortSeq;//序列号charData;//数据}ICMPHDR,*PICMPHDR;4.编程时,需要用到一些windows函数,说明如下:<1>.intWSAStartup<WORDwVersionRequested,LPWSADATAlpWSAData>;函数说明:为了在应用程序当中调用任何一个WinsockAPI函数,首先第一件事情就是必须通过WSAStartup函数完成对Winsock服务的初始化,因此需要调用WSAStartup函数。使用Socket的程序在使用Socket之前必须调用WSAStartup函数。该函数的第一个参数指明程序请求使用的Socket版本,其中高位字节指明副版本、低位字节指明主版本;操作系统利用第二个参数返回请求的Socket的版本信息。当一个应用程序调用WSAStartup函数时,操作系统根据请求的Socket版本来搜索相应的Socket库,然后绑定找到的Socket库到该应用程序中。以后应用程序就可以调用所请求的Socket库中的其它Socket函数了。<2>.SOCKETsocket<intaf,inttype,intprotocol>;函数说明:应用程序调用socket函数来创建一个能够进行网络通信的套接字。第一个参数指定应用程序使用的通信协议的协议族,对于TCP/IP协议族,该参数置AF_INET;第二个参数指定要创建的套接字类型,流套接字类型为SOCK_STREAM、数据报套接字类型为SOCK_DGRAM、原始套接字SOCK_RAW<WinSock接口并不适用某种特定的协议去封装它,而是由程序自行处理数据包以 及协议首部>;第三个参数指定应用程序所使用的通信协议。<3>.intsendto<SOCKETs,constcharFAR*buf,intlen,intflags,conststructsockaddrFAR*to,inttolen>;函数说明:返回值:实际发送数据的长度。parameter:s套接字buff待发送数据的缓冲区size缓冲区长度Flags调用方式标志位,一般为0,改变Flags,将会改变Sendto发送的形式addr〔可选指针,指向目的套接字的地址lenaddr所指地址的长度<4>intrecvfrom<SOCKETs,charFAR*buf,intlen,intflags,structsockaddrFAR*from,intFAR*fromlen>;函数说明:recvfrom<>用来接收远程主机经指定的socket传来的数据,并把数据传到由参数buf指向的内存空间,参数len为可接收数据的最大长度.参数flags一般设0,其他数值定义参考recv<>.参数from用来指定欲传送的网络地址,结构sockaddr请参考bind<>函数.参数fromlen为sockaddr的结构长度.二.程序的流程图和源码Ping程序的设计与实现大致可分为四个模块〔见图1-1,分别是:初始化模块、功能控制模块、ping模块、mian测试模块。PPing程序的设计与实现初始化模块功能控制模块Ping模块main测试模块图1-11.初始化模块:该模块用于定义及初始化各个全局变量,为winsock加载winsock体。〔见图1-2主要包括定义IP首部格式、定义ICMP首部格式、定义ICMP回应请求、定义ICMP回应答复。初始化模块初始化模块定义IP首部格式定义ICMP首部格式定义ICMP回应请求定义ICMP回应答复图1-22.功能控制模块:该模块是被其他模块调用,其功能包括计算校验和、发送回应请求函数、接收应答回复并进行解析、等待回应答复〔主是要select模型。〔见图1-3,1-4,1-5,1-6计算校验和函数源码:否否<将反馈的16位从高位移到低位>是<使用32位累加器,进行16位的反馈计算>Checksum<>开始定义初始化一些变量Nleft>1sum+=*w++;nleft-=2;Nleft=1是<补全奇数位>sum+=u;sum=<sum>>16>+<sum&0xffff>;sum+=<sum>>16>;answer=~sum;返回answer结束图1-3是是SendEchoRequest<>开始定义初始化一些变量填充要发送的数据存储发送的时间计算回应请求的校验和发送回应请求返回nRet调用发送错误函数结束图1-4WaitForEchoReply<>开始WaitForEchoReply<>开始定义初始化一些变量返回timeout结束是RecvEchoReply<>开始定义初始化一些变量接收应答回复检验接收结果返回应答时间调用发送错误函数结束图1-6图1-53.Ping模块功能模块:该模块是本程序的核心模块,调用其他模块实现其功能,进而实现Ping的功能。s否否是是开始定义初始化各个局部变量判断WSAGetLastError<>是否调用成功否检测目标主机是否为NULL设置目标主机的IP地址,开始ping发起四次ping测试发送ICMP回应请求等待回复的数据接收回复计算花费时间Loop是否为0输出平均次数输出ping结果清除残余结束4.main<>函数模块:向指定的域名或IP地址发送Echo请求报文;根据响应报文显示出Ping的结果;程序仅支持-t选项即可。否否开始WSAStartup<>是否成功截取后三位字符调用ping<>释放资源加载失败结束为了实现-t图1-8三.运行操作及结果在VC中运行程序后会出现如图4.1所示,提示你输入IP或网址;2、我们先输入校园网机房主机命令,看能否ping通3.试着使用ping–t命令,如下:4、再输入外部网主机命令,看能否ping通,上图为网络ping不通的情形。源代码源代码如下:#include<stdio.h>#include<stdlib.h>#include<winsock.h>#include<conio.h>#pragmacomment<lib,"ws2_32.lib">//导入库文件#defineICMP_ECHOREPLY0//ICMP回应答复#defineICMP_ECHOREQ8//ICMP回应请求#defineREQ_DATASIZE32//请求数据报大小#include<iostream>/*******************/usingnamespacestd;//定义IP首部格式typedefstruct_IPHeader{ u_charVIHL;//版本和首部长度 u_charToS;//服务类型 u_shortTotalLen;//总长度 u_shortID;//标识号 u_shortFrag_Flags;//片偏移量 u_charTTL;//生存时间 u_charProtocol;//协议 u_shortChecksum;//首部校验和 structin_addrSrcIP;//源IP地址 structin_addrDestIP;//目的地址}IPHDR,*PIPHDR;//定义ICMP首部格式typedefstruct_ICMPHeader{ u_charType;//类型 u_charCode;//代码 u_shortChecksum;//首部校验和 u_shortID;//标识 u_shortSeq;//序列号 charData;//数据}ICMPHDR,*PICMPHDR;//定义ICMP回应请求typedefstruct_ECHOREQUEST{ ICMPHDRicmpHdr; DWORDdwTime; charcData[REQ_DATASIZE];}ECHOREQUEST,*PECHOREQUEST;//定义ICMP回应答复typedefstruct_ECHOREPLY{ IPHDRipHdr; ECHOREQUESTechoRequest; charcFiller[256];}ECHOREPLY,*PECHOREPLY;/**********************************************************************************///计算校验和u_shortchecksum<u_short*buffer,intlen>{ registerintnleft=len; registeru_short*w=buffer; registeru_shortanswer; registerintsum=0; //使用32位累加器,进行16位的反馈计算 while<nleft>1> { sum+=*w++; nleft-=2; } //补全奇数位 if<nleft==1> { u_shortu=0; *<u_char*><&u>=*<u_char*>w; sum+=u; } //将反馈的16位从高位移到低位 sum=<sum>>16>+<sum&0xffff>; sum+=<sum>>16>; answer=~sum; return<answer>;}/**********************************************************************************************///发送回应请求函数intSendEchoRequest<SOCKETs,structsockaddr_in*lpstToAddr>{ staticECHOREQUESTechoReq; staticnId=1; staticnSeq=1; intnRet; //填充回应请求消息 echoReq.icmpHdr.Type=ICMP_ECHOREQ; echoReq.icmpHdr.Code=0; echoReq.icmpHdr.Checksum=0; echoReq.icmpHdr.ID=nId++; echoReq.icmpHdr.Seq=nSeq++; //填充要发送的数据 for<nRet=0;nRet<REQ_DATASIZE;nRet++> { echoReq.cData[nRet]='1'+nRet; } //存储发送的时间 echoReq.dwTime=GetTickCount<>; //计算回应请求的校验和 echoReq.icmpHdr.Checksum=checksum<<u_short*>&echoReq,sizeof<ECHOREQUEST>>; //发送回应请求 nRet=sendto<s,<LPSTR>&echoReq,sizeof<ECHOREQUEST>, 0,<structsockaddr*>lpstToAddr,sizeof<SOCKADDR_IN>>; if<nRet==SOCKET_ERROR> { printf<"sendto<>error:%d\n",WSAGetLastError<>>; }return<nRet>;}/*******************************************************************************///接收应答回复并进行解析DWORDRecvEchoReply<SOCKETs,LPSOCKADDR_INlpsaFrom,u_char*pTTL>{ ECHOREPLYechoReply; intnRet; intnAddrLen=sizeof<structsockaddr_in>; //接收应答回复 nRet=recvfrom<s,<LPSTR>&echoReply,sizeof<ECHOREPLY>, 0,<LPSOCKADDR>lpsaFrom,&nAddrLen>; //检验接收结果 if<nRet==SOCKET_ERROR> { printf<"recvfrom<>error:%d\n",WSAGetLastError<>>; } //记录返回的TTL *pTTL=echoReply.ipHdr.TTL; //返回应答时间 return<echoReply.echoRequest.dwTime>;}/********************************************************************************///等待回应答复,使用select模型intWaitForEchoReply<SOCKETs>{ structtimevaltimeout; fd_setreadfds; readfds.fd_count=1; readfds.fd_array[0]=s; timeout.tv_sec=1; timeout.tv_usec=0; return<select<1,&readfds,NULL,NULL,&timeout>>;}/******************************************************************************///PING功能实现voidPing<char*pstrHost,boollogic>{ charc; SOCKETrawSocket; LPHOSTENTlpHost; structsockaddr_indestIP; structsockaddr_insrcIP; DWORDdwTimeSent; DWORDdwElapsed; u_charcTTL; intnLoop,k=4; intnRet,minimum=100000,maximum=0,average=0; intsent=4,reveived=0,lost=0; //创建原始套接字,ICMP类型 rawSocket=socket<AF_INET,SOCK_RAW,IPPROTO_ICMP>;//第二个注释函数socket if<rawSocket==SOCKET_ERROR> { printf<"socket<>error:%d\n",WSAGetLastError<>>; return; } //检测目标主机 lpHost=gethostbyname<pstrHost>; if<lpHost==NULL> { printf<"Hostnotfound:%s\n",pstrHost>; return; } //设置目标机地址 destIP.sin_addr.s_addr=*<<u_longFAR*><lpHost->h_addr>>;//设置目标IP destIP.sin_family=AF_INET;//地址规格 destIP.sin_port=0; //提示开始进行PING printf<"\nPinging%s[%s]with%dbytesofdata:\n",pstrHost,inet_ntoa<destIP.sin_addr>,REQ_DATASIZE>; //发起多次PING测试 for<nLoop=0;nLoop<k;nLoop++>{ if<logic>k=k+1; //发送ICMP回应请求 SendEchoRequest<rawSocket,&destIP>; //等待回复的数据 nRet=WaitForEchoReply<rawSocket>; if<nRet==SOCKET_ERROR> { printf<"select<>error:%d\n",WSAGetLastError<>>; break; } if<!nRet> { lost++; printf<"\nRequesttimeout.">; continue; } //接收回复 dwTimeSent=RecvEchoReply<rawSocket,&srcIP,&cTTL>; reveived++; //计算花费的时间 dwElapsed=GetTickCount<>-dwTimeSent; if<dwElapsed>maximum>maximum=dwElapsed; if<dwElapsed<minimum>minimum=dwElapsed; average+=dwElapsed; printf<"\nReplyfrom%s:bytes=%dtime=%ldmsTTL=%d", inet_ntoa<srcIP.sin_addr>,REQ_DATASIZE,dwElapsed,cTTL>; if<_kbhit<>>/*Use_getchtothrowkeyaway.*/ {if<<c=_getch<>>==0x2>//crrl-bbreak; }else Sleep<1000>; } printf<"\n\n">; printf<"Pingstatisticsfor%s:\n",inet_ntoa<srcIP.sin_addr>>; printf<"Packets:Sent=%d,Received=%d,Lost=%d<%.f%%loss>,\n", sent,reveived,lost,<float><lost*1.0/sent>*100>; if<lost==0> { printf<"Approximateroundtriptimesinmilli-seconds:\n">; printf<"Minimum=%dms,Maximum=%dms,Average=%dms\n",minimum,maximum,average/sent>; } printf<"\n\n">; nRet=closesocket<rawSocket>; if<nRet==SOCKET_ERROR> { printf<"closesocket<>error:%d\n",WSAGetLastError<>>; }}//主程序voidmain<>{ printf<"WelcometothePingTest\n">; while<1> { WSADATAwsd;//检测输入的参数 //初始化Winsock if<WSAStartup<MAKEWORD<1,1>,&wsd>!=0>{//第一个函数说明WSAStartup<> printf<"加载Winsock失败!\n">; } charopt1[100]; char*ptr=opt1; boollog=false; printf<"Ping">; cin.getline<opt1,100,'\n'>;//ping的地址字符串 if<strstr<opt1,"-t">!=NULL> { log=true; strncpy<ptr,opt1+0,strlen<opt1>-3>;//把原字符串的最后三位截取 ptr[strlen<opt1>-2]=0;//printf<"%s",ptr>; } //开始PING Ping<ptr,log>; //程序释放Winsock资源 WSACleanup<>; }}/*函数说明为了在应用程序当中调用任何一个WinsockAPI函数,首先第一件事情就是必须通过WSAStartup函数完成对Winsock服务的初始化,因此需要调用WSAStartup函数。使用Socket的程序在使用Socket之前必须调用WSAStartup函数。该函数的第一个参数指明程序请求使用的Socket版本,其中高位字节指明副版本、低位字节指明主版本;操作系统利用第二个参数返回请求的Socket的版本信息。当一个应用程序调用WSAStartup函数时,操作系统根据请求的Socket版本来搜索相应的Socket库,然后绑定找到的Socket库到该应用程序中。以后应用程序就可以调用所请求的Socket库中的其它Socket函数了。*//*函数说明SOCKETsocket<intaf,inttype,intprotocol>;应用程序调用socket函数来创建一个能够进行网络通信的套接字。第一个参数指定应用程序使用的通信协议的协议族,对于TCP/IP协议族,该参数置AF_INET;第二个参数指定要创建的套接字

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论