Ping程序设计Word文档格式.docx
《Ping程序设计Word文档格式.docx》由会员分享,可在线阅读,更多相关《Ping程序设计Word文档格式.docx(27页珍藏版)》请在冰豆网上搜索。
计算机网络
专业:
软件工程
班级:
09级01班
学号:
200922146159
姓名:
丁勇
指导老师:
李鹏
基于Socket的PING程序设计
ping命令是使用频率极高的一个网络测试命令,用以测试从一个主机到另一个主机间的网络上否可达。
windows自带的ping命令具有强大的功能,它有很多选项用于实现不同的测试目的。
本章模仿windows的ping命令,用c语言实现了一个简单的命令。
本章着重讲述ping命令的实现原理和c语言的网络编程方法。
读者可以在本章的基础上,对本章实现的ping命令进行扩展,开发出功能更强大、更完善的ping命令,并进一步掌握网络编程的方法。
1、设计目的
本章通过设计Ping程序,讲解Ping程序的实现原理,并初步讲解了c语言网络编程技术。
本章涉及很多网络编程函数和编程技巧。
包括库文件的导入;
winsock的初始化、注销;
socket的创建、关闭;
设置socket选项;
根据主机名获取IP地址;
从堆中分配一定数量的空间、释放从堆中分配的空间;
获取当前进程ID号;
数据报的发送;
数据报的接等。
通过本程序的训练,使读者对网络编程有一定的了解,掌握Ping程序的设计方法,掌握网络编程的方法和技巧,从而编写出功能更强大的程序。
2、功能描述
本章用c语言实现的ping命令,能用于测试一个主机到另一个主机间的联通情况,程序还提供了几个选项以实现不同的功能。
(1)实现ping功能。
程序能实现基本的ping操作,发送ICMP回显请求报文,接收应答报文。
(2)能记录路由。
程序提供了“-r”选项,用以记录从源主机到目的主机的路由。
(3)能输出指定条数的记录。
程序提供了“-n”选项,用以输出指定条数的记录。
(4)能按照指定大小输出每条记录。
程序提供了“datasize”选项,用以指定输出的数据报的大小。
(5)能输出用户帮助。
程序提供了用户帮助,显示程序提供的选项以及选项格式等。
3、总体设计
3.1、功能模块设计
3.1.1、功能模块图
本系统共有4个模块,分别是初始化模块、功能控制模块、数据控制模块、数据报解读模块和ping测试模块,如图9.1所示。
各模块功能描述如下。
系统模块图
(1)初始化模块。
该模块用于初始化各个全局变量,为全局变量赋初始值;
初始化,加载库。
(2)功能控制模块。
该模块是被其它模块调用,其功能包括获取参数、计算校验和填充数据报文、释放占用资源和显示用户帮助。
(3)数据报解读模块。
该模块用于解读接收到的报文和选项。
(4)测试模块。
该模块是本程序的核心模块,调用其他模块实现其功能,主要是实现的功能。
3.1.2、系统流程图
系统执行的流程图9.2所示。
程序首先调用IniPing()函数初始化各全局变量,然后GetArgments()函数获取用户输入的参数,检查用户输入的参数,如果参数不正确或者没有输入参数,则显示用户帮助信息(Userhelp),并结束程序;
如果参数正确,则对指定目的地执行Ping命令,如果Ping通,则显示Ping结果并释放占用资源,如果没有Ping通,则报告错误信息,并释放占用资源。
图9.2系统流程图
3.1.3、参数获取(GetArgments()函数)流程图
获取的参数包括“-r”(记录路由)、“-n”(记录条数程序,任意的整数)和datasize(数据报大小)。
程序首先判断每一个参数的第一字符,如果第一个字符是“-”(短横线),则认为是“-r”或者“-n”中的一个,然后作进一步判断。
如果该参数的第二个字符是数字,则判断该参数为记录的条数,如果该参数的第二个字符是“r”,则判断该参数为“-r”,用于记录路由;
如果参数的第一个字符是数字,则认为参数是IP地址;
或者datasize,然后作进一步的判断。
如果该参数中不存在非数字的字符,则判断该参数为datasize;
如果存在非数字的字符,则判断该参数为IP地址;
其他情况则判断为主机名。
参数获取的流程如图9.3所示。
图9.3参数获取流程图
3.1.4、ping()函数流程图
ping()函数是本程序的核心部分它调用其他模块的函数来实现,其主要步骤包括创建接字,设置路由选项(如果需要的话)、设置接收和发送超时值、名字解析(如果需要的话)、分配内存、创建ICMP报文、发送ICMP请求报文、接收ICMP应答报文和解读ICMP报文。
其执行流程如图9.4所示。
图9.4Ping函数流程图
3.2、数据结构设计
本程序定义了3个结构体:
-iphdr、-icmphdr、和-ipotionhdr,分别用于存放IP报头信息、ICMP报头信息和IP路由选项信息。
3.2.1、定义IP报头结构体
Typedefstruct_iphdr
{
Unsignedinth_len:
4;
Unsignedintversion:
Unsignedchartos;
Unsignedshorttotal_len;
Unsignedshortident;
Unsignedshortfrag_flags;
Unsignedcharttl;
Unsignedchorproto;
Unsignedshortchecksum;
UnsignedintsourceIP;
UnsignedintdestIP;
}IpHeader;
其中各字段表示意义如下。
h-len:
4:
表示IP报头长度,首部长度指的是首部占32bit字的数目,包括任何选项。
由于它是一个4bit字段,因此首部最长为60个字节,不包括任何选项的IP报头是20个字节。
version:
4:
表示IP的版本号,这里表示Ipv4.。
top:
表示服务的类型,可以表示最小时延,最大吞吐量,最高可靠性和最小费用。
total–len:
整个IP数据报的总长度。
ident:
唯一的标识符,标识主机发送的每一份数据报。
frag-flags:
分段标志,表示过长的数据报是否要分段。
ttl:
生存期,表示数据报可以经过的最多路由器数。
proto:
协议类型(TCP、UDP等)。
checksum:
校验和。
sourceIP:
源IP地址。
destIP:
目的IP地址。
3.2.2、定义ICMP报头结构体
Typedefstruct–icmphdr
{
BYTEi_type;
BYTEi_code:
USHORTi_cksum;
USHORTi_id;
USHORTi_seq;
ULONGtimestamp;
}IcmpHeader;
i_type:
ICMP报文类型。
i_code:
该类型中的代码号,一种ICMP报文的类型号和该类型中的代码号共同决定。
、
i_cksum:
i_seq:
序列号,序列号从0开始,每发送一次新的回显请求就加1.
timestamp:
时间戳。
3.2.3、定义IP选项结构体
Typedefstruct_ipoptionhdr
Unsignedcharcode;
Unsignedcharlen;
Unsignedcharptr;
Unsignedloangaddr[9];
}IpOptionHeader;
其中各字段表示意义如下。
Code:
指明IP选项类型,对于路由记录选项,它的值是7。
Len:
选项头长度。
Ptr:
地址指针字段,是一个基于1的指针,指向存放下一个IP地址的位置。
addr[9]:
记录的Ip地址列表,由于IP首部中选项的空间有限,所以可以记录的Ip地址最多是9个。
3.3、函数功能描述
1)InitPing()
函数原型:
voidIntPing()
InitPing()函数用于初始化ping所需的全局变量,为各个变量赋初始值。
2)userHelp()
voiduserHelp()
userHelp()函数用于显示用户帮助信息。
当程序检查到参数错误或者没有必要的参数(如主机IP地址或者主机名)时,则会调用此函数显示帮助信息。
3)GetArgments()
voidGetArgments(intargc,char**argv)
GetArgments()函数用于获取用户提交的参数。
其中argc表示获取的参数个数,argv用于存储获取的参数,这两个形参和主函数中的形参表示的意义一样的。
4)checkSum()
USHORTcheckSum(USHORT*buffer,intsize)
checkSum()函数用于计算校验和。
计算过程是首先把数据报头中的校验和字段设置
为0,然后对首部中每个16bit进行二字段进制反码求和(整个首部看成是由一串16bit的字组成),结果存在校验和字段中。
其中buffer用于存放ICMP数据,size表示ICMP报文大小。
5)FillCMPData()
voidFillCMPData()
FillCMPData()函数用于填充ICMP数据报中各个字段。
其中icmp_data表示ICMP数据,datasize表示ICMP报文大小。
6)FreeRes()
voidFreeRes()
FreeRes()函数用于释放占用的资源,包括关闭初始化socket调用的函数的、关闭创建的socket和释放分配的内存等。
7)DecodeIPOptions()
voidDecodeIPOptions()
DecodeIPOptions()函数用于解读IP选项,从中读出从源主机到目的主机经过的路由,并输出路由信息。
Buf表示存放接收到的ICMP报文的缓冲区,bytes表示接收到的字节数。
8)DecodeICMPHeader()
voidDecodeICMPHeader(char*buf,intbytes,SOCKADDR_IN*from)
DecodeICMPHeader()函数用于解读ICMP报文信息。
Buf表示存放接收到的ICMP报文的缓冲区,bytes表示接收到的字节数,from表示发送ICMP回显应答的主机IP地址。
9)PingTest()
voidPingTest(inttimeout)
PingTest()函数用于进行Ping操作。
其中timeout表示设定的发送超时值。
3.4、程序实现
3.4.1、源码分析
1.程序预处理
/*导入库文件*/
#pragmacomment(lib,"
ws2_32.lib"
)
/*加载头文件*/
#include<
winsock2.h>
ws2tcpip.h>
stdio.h>
stdlib.h>
math.h>
/*定义常量*/
/*表示要记录路由*/
#defineIP_RECORD_ROUTE0x7
/*默认数据报大小*/
#defineDEF_PACKET_SIZE32
/*最大的ICMP数据报大小*/
#defineMAX_PACKET1024
/*最大IP头长度*/
#defineMAX_IP_HDR_SIZE60
/*ICMP报文类型,回显请求*/
#defineICMP_ECHO8
/*ICMP报文类型,回显应答*/
#defineICMP_ECHOREPLY0
/*最小的ICMP数据报大小*/
#defineICMP_MIN8
/*自定义函数原型*/
voidInitPing();
voidUserHelp();
voidGetArgments(intargc,char**argv);
USHORTCheckSum(USHORT*buffer,intsize);
voidFillICMPData(char*icmp_data,intdatasize);
voidFreeRes();
voidDecodeIPOptions(char*buf,intbytes);
voidDecodeICMPHeader(char*buf,intbytes,SOCKADDR_IN*from);
voidPingTest(inttimeout);
/*IP报头字段数据结构*/
typedefstruct_iphdr
unsignedinth_len:
/*IP报头长度*/
unsignedintversion:
/*IP的版本号*/
unsignedchartos;
/*服务的类型*/
unsignedshorttotal_len;
/*数据报总长度*/
unsignedshortident;
/*惟一的标识符*/
unsignedshortfrag_flags;
/*分段标志*/
unsignedcharttl;
/*生存期*/
unsignedcharproto;
/*协议类型(TCP、UDP等)*/
unsignedshortchecksum;
/*校验和*/
unsignedintsourceIP;
/*源IP地址*/
unsignedintdestIP;
/*目的IP地址*/
/*ICMP报头字段数据结构*/
typedefstruct_icmphdr
/*ICMP报文类型*/
BYTEi_code;
/*该类型中的代码号*/
/*校验和*/
/*序列号*/
/*时间戳*/
/*IP选项头字段数据结构*/
typedefstruct_ipoptionhdr
unsignedcharcode;
/*选项类型*/
unsignedcharlen;
/*选项头长度*/
unsignedcharptr;
/*地址偏移长度*/
unsignedlongaddr[9];
/*记录的IP地址列表*/
/*定义全局变量*/
SOCKETm_socket;
IpOptionHeaderIpOption;
SOCKADDR_INDestAddr;
SOCKADDR_INSourceAddr;
char*icmp_data;
char*recvbuf;
USHORTseq_no;
char*lpdest;
intdatasize;
BOOLRecordFlag;
doublePacketNum;
BOOLSucessFlag;
2.初始化模块
/*初始化变量函数*/
voidInitPing()
WSADATAwsaData;
icmp_data=NULL;
seq_no=0;
recvbuf=NULL;
RecordFlag=FALSE;
lpdest=NULL;
datasize=DEF_PACKET_SIZE;
PacketNum=5;
SucessFlag=FALSE;
/*Winsock初始化*/
if(WSAStartup(MAKEWORD(2,2),&
wsaData)!
=0)
{
/*如果初始化不成功则报错,GetLastError()返回发生的错误信息*/
printf("
WSAStartup()failed:
%d\n"
GetLastError());
return;
}
m_socket=INVALID_SOCKET;
}
3.功能控制模块
/*显示信息函数*/
voidUserHelp()
UserHelp:
ping-r<
host>
[datasize]\n"
);
-rrecordroute\n"
-nrecordamount\n"
hostremotemachinetoping\n"
datasizecanbeupto1KB\n"
ExitProcess(-1);
/*获取ping选项函数*/
voidGetArgments(intargc,char**argv)
inti;
intj;
intexp;
intlen;
intm;
/*如果没有指定目的地地址和任何选项*/
if(argc==1)
\nPleasespecifythedestinationIPaddressandthepingoptionasfollow!
\n"
UserHelp();
for(i=1;
i<
argc;
i++)
len=strlen(argv[i]);
if(argv[i][0]=='
-'
)
/*选项指示要获取记录的条数*/
if(isdigit(argv[i][1]))
{
PacketNum=0;
for(j=len-1,exp=0;
j>
=1;
j--,exp++)
/*根据argv[i][j]中的ASCII值计算要获取的记录条数(十进制数)*/
PacketNum+=((double)(argv[i][j]-48))*pow(10,exp);
}
else
switch(tolower(argv[i][1]))
/*选项指示要获取路由信息*/
case'
r'
:
RecordFlag=TRUE;
break;
/*没有按要求提供选项*/
default:
UserHelp();
/*参数是数据报大小或者IP地址*/
elseif(isdigit(argv[i][0]))
{
for(m=1;
m<
len;
m++)
if(!
(isdigit(argv[i][m])))
/*是IP地址*/
lpdest=argv[i];
break;
/*是数据报大小*/
elseif(m==len-1)
datasize=atoi(argv[i]);
}
/*参数是主机名*/
else
/*求校验和函数*/
USHORTCheckSum(USHORT*buffer,intsize)
unsignedlongcksum=0;
while(size>
1)
cksum+=*buffer++;
size-=sizeof(USHORT);
if(size)
cksum+=*(UCHAR*)buffer;
/*对每个16bit进行二进制反码求和*/
cksum=(cksum>
>
16)+(cksum&
0xffff);
cksum+=(cksum>
16);
return(USHORT)(~cksum);
/*填充ICMP数据报字段函数*/
voidFillICMPData(char*icmp_data,intdatasize)
IcmpHeader*icmp_hdr=NULL;
char*datapart=NULL;
icmp_hdr=(IcmpHeader*)icmp_data;
/*ICMP报文类型设置为回显请求*/
icmp_hdr->
i_type=ICMP_ECHO;
i_code=0;
/*获取当前进程IP作为标识符*/
i_id=(USHORT)GetCurrentProcessId();
i_cksum=0;
i_seq=0;
datapart=icmp_data+sizeof(IcmpHeader);
/*以数字0填充剩余空间*/
memset(datapart,'
0'
datasize-sizeof(IcmpHeader));
/*释放资源函数*/
/*关闭创建的套接字*/
if(m_socket!
=INVALID_SOCKET)
closesocket(m_socket);
/*释放分配的内存*/
HeapFree(GetProcessHeap(),0,recvbuf);
HeapFree(GetProcessHeap(),0,icmp_data);
/*注销WSAStartup()调用*/
WSACleanup();
4.数据报解读模块
/*解读IP