设计报告.docx
《设计报告.docx》由会员分享,可在线阅读,更多相关《设计报告.docx(24页珍藏版)》请在冰豆网上搜索。
设计报告
设计报告
课程计算机网络
设计名称Ping程序的设计与实现
专业班级计科111、计科112
同组人学号姓名__________________________________
实验日期2014.3.24——2014.3.30
指导教师____
成绩
2014年3月27日
目录
一、设计目的与要求3
1.实验目的3
2.实验要求3
二、设计说明(包括设计分析,系统运行环境,设计中的重点和难点,输入和输出输出条件等)3
1.Ping的基础知识3
2.ICMP协议简介3
3.校验和的计算3
4.Windows函数说明4
5.设计步骤分析5
6.设计中的重点与难点5
三、系统详细设计(包括程序流程、主要函数等)5
1.初始化模块5
2.功能控制模块5
3.Ping模块功能模块8
4.main()函数模块9
四、程序源代码及注释9
五、实验数据、结果分析15
六、总结16
七、教师意见16
一、设计目的与要求
1.实验目的
通过设计Ping程序,理解Ping程序的实现原理,并初步讲解了c语言网络编程技术。
本章涉及很多网络编程函数和编程技巧,包括库文件的导入;winsock的初始化、注销;socket的创建、关闭;设置socket选项;根据主机名获取IP地址;从堆中分配一定数量的空间、释放从堆中分配的空间;数据报的发送;数据报的接等。
2.实验要求
利用ICMP数据包,测试主机的连通性。
通过课程设计来熟悉ICMP报文结构,对ICMP有更深的理解并输出Ping程序,命令行运行:
Pingip。
二、设计说明(包括设计分析,系统运行环境,设计中的重点和难点,输入和输出输出条件等)
1.Ping的基础知识
Ping是用于测试网络连接量的程序,利用它可以检查网络是否能够连通。
Ping程序的实现原理非常简单,即发送一个ICMP回声请求消息给目的地并报告是否能够收到所希望的ICMP回声应答。
2.ICMP协议简介
ICMP是(InternetControlMessageProtocol)网际控制报文协议。
ICMP报文的种类有两种,即ICMP差错报告报文和ICMP询问报文。
它是TCP/IP协议族的一个子协议,属于网络层协议,用于在IP主机、路由器之间传递控制消息。
控制消息是指网络不通,主机是否可达,路由是否可用等网络本身的消息。
这些控制信息虽然并不传输用户数据,但是对于用户数据的传递起着重要作用。
ICMP协议时一种面向连接的协议,用于传输出错报文控制信息。
它是一个非常重要的协议,它对于网络安全具有极其重要的意义ICMP提供一致易懂的出错报告信息,发送的出错报文返回到发送原始数据的设备,因为只有发送设备才是出错报文的逻辑接受者。
发送设备随后可根据ICMP报文确定发生错误类型,并确定如何才能更好的重发失败的数据报。
但是ICMP唯一的功能是报告问题而不是纠正错误,纠正错误的任务由发送方完成。
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);
}
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);
函数说明:
返回值:
实际发送数据的长度
s:
一个标识套接口的描述字
buf:
包含待发送数据的缓冲区
len:
buf缓冲区中数据的长度
flags:
调用方式标志位
to:
可选指针,指向目的套接口的地址
tolen:
to所指地址长度
(4)intrecvfrom(SOCKETs,charFAR*buf,intlen,intflags,structsockaddrFAR*from,intFAR*fromlen);
函数说明:
recvfrom()用来接收远程主机经指定的socket传来的数据,并把数据传到由参数buf指向的内存空间,参数len为可接收数据的最大长度。
参数flags一般设0,其他数值定义参考recv().参数from用来指定欲传送的网络地址,结构sockaddr请参考bind()函数,参数fromlen为sockaddr的结构长度。
5.设计步骤分析
要实现ping程序,需要实现以下步骤:
(1)创建协议类型为IPPROTO_ICMP的原始套接字,设置套接字属性。
(2)创建并初始化ICMP封包。
(3)调用sendto函数向远程主机发送ICMP请求。
(4)调用recfrom函数接受ICMP响应。
初始化ICMP头时先初始化消息类型和代码域,之后是回显请求头。
程序首先定义了ICMP头的数据结构IMCP_HDR.。
ICMP_HDR的定义如下:
typedefstruct_ICMPHeader
{
u_charType;//类型
u_charCode;//代码
u_shortChecksum;//首部校验和
u_shortID;//标识
u_shortSeq;//序列号
charData;//数据
}ICMPHDR,*PICMPHDR;
6.设计中的重点与难点
ICMP数据包的打包和解包,以及从CRC16校验算法的分析实现
三、系统详细设计(包括程序流程、主要函数等)
Ping程序的设计与实现大致可分为四个模块:
初始化模块、功能控制模块、ping模块、mian测试模块。
1.初始化模块
该模块用于定义及初始化各个全局变量,为winsock加载winsock体主要包括定义IP首部格式、定义ICMP首部格式、定义ICMP回应请求、定义ICMP回应答复。
2.功能控制模块
该模块是被其他模块调用,其功能包括计算校验和、发送回应请求函数、接收应答回复并进行解析、等待回应答复(主是要select模型)。
(见图1-1,1-2,1-3,1-4)
计算校验和函数源码:
图1—1计算校验和
图1—2发送回应请求函数
图1—3接收应答回复并进行解析
图1—4等待回应答复
3.Ping模块功能模块
该模块是本程序的核心模块,调用其他模块实现其功能,进而实现Ping的功能。
4.main()函数模块
向指定的域名或IP地址发送Echo请求报文;根据响应报文显示出Ping的结果;程序仅支持ping-t选项即可。
四、程序源代码及注释
源代码如下:
#include
#include
#include
#include
#pragmacomment(lib,"ws2_32.lib")//导入库文件
#defineICMP_ECHOREPLY0//ICMP回应答复
#defineICMP_ECHOREQ8//ICMP回应请求
#defineREQ_DATASIZE32//请求数据报大小
#include/*******************/
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{
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;nLoopif(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(dwElapsedaverage+=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-b
break;
}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());
}
}
/