c++实现ping功能.docx
- 文档编号:5550385
- 上传时间:2022-12-19
- 格式:DOCX
- 页数:17
- 大小:127.96KB
c++实现ping功能.docx
《c++实现ping功能.docx》由会员分享,可在线阅读,更多相关《c++实现ping功能.docx(17页珍藏版)》请在冰豆网上搜索。
c++实现ping功能
题目:
VC++实现探测远程主机是否可达
1课程设计目的
(1)了解探测远程主机是否可达的方法。
(2)了解windows网络编程的基本步骤。
(3)了解TCP/IP协议及ICMP协议。
(4)掌握ping命令的思想。
(5)掌握WindowsSocketAPI的使用方法。
2课程设计原理
ICMP:
互联网控制报文协议,提供一个特殊的报文在互联网的结点之间进行监测和控制,测试目的主机是否可达,与本次课程设计目的一致,因此可模拟ICMP的工作方式来编制程序。
首先通过生成一个ICMP“回应请求”,然后将其定向至打算查询的目标主机,远程主机会拦截这个请求,然后生成一条回应答复消息,再通过网络回传给我们。
若是不能抵达目标主机,则会生成相应的错误消息。
具体步骤如下:
1)创建类型为SOCK_RAW的一个套接字,同时设定协议IPPROTO_ICMP。
2)创建并初始化ICMP头。
3)调用sendto将ICMP请求发给远程主机。
4)调用recvfrom以接收ICMP响应。
其流程图如下:
3程序源代码及运行结果
3.1源代码
#include
#include
#pragmacomment(lib,"ws2_32.lib")
#defineICMP_ECHO8//回应请求类型的ICMP消息类型
#defineICMP_ECHOREPLY0//回应答复型的ICMP消息类型
#defineICMP_MIN8//ICMP数据报最小长度
#defineDEF_PACKET_SIZE32//默认数据报大小
#defineDEF_PACKET_NUMBER4//发送数据报的个数
#defineMAX_PACKET1024//最大ICMP数据报大小
char*Ipdest;//目标主机的IP地址
intdatasize;//ICMP消息的长度
//接收到的ICMP消息数,每收到一条ICMP消息就加1
staticinticmpcount=0;
//IP头(20字节)
structIPHEAD
{
unsignedintheadlength:
4;//4位头长度
unsignedintversion:
4;//4位版本
unsignedchartos;//8位服务类型
unsignedshorttotallength;//16位总长
unsignedshortip_id;//16位标识
unsignedshortfrag_and_flags;//3个一位标识加分段偏移
unsignedcharttl;//8位存活时间
unsignedcharprotocal;//8位协议类型
unsignedshortip_checksum;//16位头校验和
unsignedintsourceIP;//32位源IP地址
unsignedintdestIP;//32位目标IP地址
};
//ICMP头(12字节)
structICMPHEAD
{
unsignedchartype;//类型(0,8)
unsignedcharcode;//代码(0)
unsignedshorticmp_checksum;//校验和
unsignedshorticmp_id;//标识符
unsignedshortseq;//序号
unsignedlongtimestamp;//时间戳
};
//Fill_icmp_data函数:
用于设置ICMP头部,填充ICMP消息
voidFill_icmp_data(char*icmp_data,intdatasize)
{
ICMPHEAD*icmphead=NULL;
char*datapart=NULL;
icmphead=(ICMPHEAD*)icmp_data;
icmphead->type=ICMP_ECHO;//ICMP_ECHO=8
icmphead->code=0;//类型为8,代码为0,表示回应请求
icmphead->icmp_checksum=0;
icmphead->icmp_id=(unsignedshort)GetCurrentProcessId();
datapart=icmp_data+sizeof(ICMPHEAD);//具体内容的首地址指针
//初始化数据包内容部分
for(inti=0;i<(datasize-sizeof(ICMPHEAD));i++,datapart++)
*datapart=0;
}
//DecodeICMPHEAD函数:
接受数据时从IP头中获取ICMP消息
voidDecodeICMPHEAD(char*recvbuf,intbread,sockaddr_in*from)
{
IPHEAD*iphead=NULL;
ICMPHEAD*icmphead=NULL;
DWORDtick;
unsignedshortiphdrlen;
iphead=(IPHEAD*)recvbuf;
//32位字的个数乘以4即字节个数
iphdrlen=(iphead->headlength)*4;
//获取操作系统启动至今所经过的时间(ms)
tick=GetTickCount();
if(bread<(iphdrlen+ICMP_MIN))
{
cout<<"Toofewbytesfrom:
"<
cout< } icmphead=(ICMPHEAD*)(recvbuf+iphdrlen);//ICMP消息始于IP头之后 //若ICMP消息类型并非查询,则输出相应信息类型 if(icmphead->type! =ICMP_ECHOREPLY) { cout<<"nonechotype"< cout< } //确认收到的应答ICMP消息是否是对发送出去的消息的回应 //若其表示与当前进程不符,则输出出错信息 if(icmphead->icmp_id! =(unsignedshort)GetCurrentProcessId()) { cout<<"It'ssomeoneelse'spacket! "< return; } //输出收到信息的内容: 主机地址,icmp消息序号,回应时间,存活时间 cout<<"Replyfrom"< cout<<"bytes="< cout<<"time: "< cout<<"seq="< cout< icmpcount++; } //Checksum函数: 用于求ICMP首部校验和 unsignedshortChecksum(unsignedshort*buffer,intsize) { unsignedlongcksum=0;//设校验和初值为0 while(size>1) { cksum+=*buffer++;//求各个16位数字之和 size-=sizeof(unsignedshort); } if(size) { cksum+=*(unsignedchar*)buffer; } cksum=(cksum>>16)+(cksum&0xffff);//高位与低位相加 cksum+=(cksum>>16);//加上进位 return(unsignedshort)(~cksum);//取反得到校验和 } //Usage函数: 表示程序的功能 voidUsage(char*progname) { cout<<"Usage: "< cout<<"hostremotemachinetoPing"< cout< } //ValidateArgs函数: 用于给目的地址和数据包大小赋值 voidValidateArgs(intargc,char**argv) { datasize=DEF_PACKET_SIZE;//数据包大小为DEF_PACKET_SIZE=32 if(argc<2) { Usage(argv[0]);//只输入一个参数: 程序名 Ipdest="202.119.81.120";//默认目的地址为: 202.119.81.120 } elseif(argc==2) Ipdest=argv[1];//第二个参数为目的地址 } //main函数: 进行建立套接字,创建ICMP头,设置超时时间,发送和接受数据等。 intmain(intargc,char**argv) { WSADATAwsaData; SOCKETsockRaw=INVALID_SOCKET; sockaddr_indest,from; intdestlen=sizeof(dest), fromlen=sizeof(from), bwrote,bread; char*icmp_data=NULL, *recvbuf=NULL; structhostent*hp=NULL; unsignedshortseq=0; //启动winsock失败,输出错误提示信息 if(WSAStartup(MAKEWORD(2,2),&wsaData)! =0) { cout<<"WSAStartupfailed: "< cout< return-1; } ValidateArgs(argc,argv); //创建套接字失败,输出错误信息提示 sockRaw=WSASocket(AF_INET,SOCK_RAW,IPPROTO_ICMP, NULL,0,WSA_FLAG_OVERLAPPED); if(sockRaw==INVALID_SOCKET) { cout<<"WSASocket()failed: "< cout< return-1; } //设定发送数据时最长等待时间timeout=1000ms inttimeout=1000; bread=setsockopt(sockRaw,SOL_SOCKET,SO_RCVTIMEO, (char*)&timeout,sizeof(timeout)); if(bread==SOCKET_ERROR) { cout<<"setsockopt(SO_RCVTIMEO)failed: "< cout< return-1; } //设定接收数据时最长等待时间timeout=1000ms timeout=1000; bread=setsockopt(sockRaw,SOL_SOCKET,SO_SNDTIMEO, (char*)&timeout,sizeof(timeout)); if(bread==SOCKET_ERROR) { cout<<"setsockopt(SO_SNDTIMEO)failed: "< cout< return-1; } //解析目的地址的名称 dest.sin_family=AF_INET; if(dest.sin_addr.s_addr=inet_addr(Ipdest)) { //inet_addr将IP地址转换成网络地址 if((hp=gethostbyname(Ipdest))! =NULL) { //gethostbyname主机信息 memcpy(&(dest.sin_addr),hp->h_addr,hp->h_length);//复制内存 dest.sin_family=hp->h_addrtype; cout<<"dest.sin_addr="< cout< } else { cout<<"gethostbyname()failed: "< cout< return-1; } } //建立一个ICMP数据包 datasize+=sizeof(ICMPHEAD); //为要发送的数据申请内存空间,最大值为1024 icmp_data=newchar[MAX_PACKET]; //为要接收的数据申请内存空间,最大值为1024 recvbuf=newchar[MAX_PACKET]; //分配内存失败,输出提示信息 if(! icmp_data) { cout<<"newchar[]failed: "< cout< return-1; } //将ICMP信息所在空间清零 memset(icmp_data,0,MAX_PACKET); //设置ICMP信息头部 Fill_icmp_data(icmp_data,datasize); //发送和接收数据,循环四次 for(inti=0;i<4;i++) { //ICMP信息封装 ((ICMPHEAD*)icmp_data)->icmp_checksum=0; ((ICMPHEAD*)icmp_data)->timestamp=GetTickCount(); ((ICMPHEAD*)icmp_data)->seq=seq++; ((ICMPHEAD*)icmp_data)->icmp_checksum= Checksum((unsignedshort*)icmp_data,datasize); //发送数据 bwrote=sendto(sockRaw,icmp_data,datasize,0, (sockaddr*)&dest,destlen); if(bwrote==SOCKET_ERROR) { if(WSAGetLastError()==WSAETIMEDOUT) { cout<<"Requesttimedout."< continue; } else { cout<<"sendto()failed: "< cout< return-1; } } //显示实际发送的字节数 cout<<"Pinging"< cout< "; cout< //接收数据 bread=recvfrom(sockRaw,recvbuf,MAX_PACKET,0, (sockaddr*)&from,&fromlen); if(bread==SOCKET_ERROR) { if(WSAGetLastError()==WSAETIMEDOUT) { cout<<"Requesttimedout."< continue; } cout<<"recvfrom()failed: "< cout< return-1; } //打开接收到的数据,从中提取ICMP头信息 DecodeICMPHEAD(recvbuf,bread,&from); //等待1s钟 Sleep(1000); } //输出发送的数据包数、接收的数据包数和丢失的数据包数 cout< cout<<"Pingstatisticsfor"< "< cout<<"Packets: sent=4,"; cout<<"Received="< cout<<",Lost="< cout<<"("<<((float)(DEF_PACKET_NUMBER-icmpcount)/DEF_PACKET_NUMBER)*100; cout<<"%loss)"; cout< //释放资源,关闭winsock if(sockRaw! =INVALID_SOCKET) closesocket(sockRaw); delete[]recvbuf; delete[]icmp_data; WSACleanup(); return0; } 3.2运行结果 点击“开始”——“运行”——“cmd”,输入“C: \program1\Debug\zpp.exe+目标主机地址”即可运行程序。 3.2.1不输入目标主机地址时 程序默认的目标地址为: “202.119.81.120”,目标主机不可达。 3.2.2输入目标主机地址为“127.0.0.1” “127.0.0.1”为本地的Lookback地址,能够ping通表明TCP/IP协议没有问题。 3.2.3输入目标主机地址为域名地址“” 可见目标主机不可达。 4获取目的地址的另一种方法及结果 4.1代码 用以下函数代替原先的ValidateArgs函数,运行程序时可输入目的地址,而不用在操作系统命令行中输入,主函数不带参数。 voidValidate(charinput[100]) { datasize=DEF_PACKET_SIZE; inti,j,k,l=0; for(i=0;i<99;i++)//找到"ping"这个字符串 { if(input[i]=='p'&&input[i+1]=='i'&&input[i+2]=='n'&&input[i+3]=='g') { j=i; break; } continue; } //"ping"之后是远程主机的地址 for(i=j+4;i<99;i++) { charkk=input[i]; //地址以数字开头为IP地址,以字母开头为域名地址 if(kk>='0'&&kk<='9'||kk>='a'&&kk<='z') { k=i; break; } continue; } //将目标地址赋给dest数组: 预先定义的字符串数组 for(i=k;input[i]! ='\0';i++) { dest[l]=input[i]; l++; } for(i=l;i<79;i++) dest[i]='\0'; Ipdest=dest;//Ipdest指向地址 cout< } 4.2运行结果 将程序编译,组建,执行,然后从键盘上输入“ping”+目的地址。 运行结果与从命令行输入目的地址一样。 4.2.1输入目标主机地址为“127.0.0.1” “127.0.0.1”为本地的Lookback地址,能够ping通表明TCP/IP协议没有问题。 4.2.2输入目标主机地址为域名地址“” 可见目标主机不可达。 5心得 刚刚拿到课题时,我只能说知道其中的ping怎么用。 至于编程来实现它,则没什么头绪。 课程设计的要求是采用WindowsSocketAPI的相关函数实现。 但是对于WindowsSocketAPI,我知之甚少。 于是去图书馆借了好几本书,在网上下载了资料无数,开始了三周的编程生涯。 过程中遇到过很多一开始看似不可克服的难题。 网络编程中有各种现成的函数,只要包含相应的头文件和库都可以调用,但是对于不熟悉的人来说,其中的返回值和数据类型并不好掌握,当几个函数的返回值和参数类型在网络字节和点式地址之间变来变去的时候,除了觉得头晕还是觉得头晕。 还有就是WindowsSocket中容易出现各种错误,需要兼顾到还是有些繁琐的。 在使用带参数的主函数的问题上,我纠结了很久,但是还是没有弄懂,最后还是在众多网友的七嘴八舌中间找到了线索,终于得到了正确的结果。 回顾这次课程设计,我感慨颇多。 在这段时间内,需要经常对着电脑,经常是坐了半天也没有成果,可以说是苦多于甜,但是这不仅可以帮助我巩固曾经学习过的知识,而且让我增长了许多课本上学不到的和自己平时不注意积累的知识。 在这个过程中,不仅我独立思考的能力得到了很大地提高,更重要的是我学到了更多学习的方法,这是日后最实用的,真的让我受益匪浅。 同时我也感觉到查阅资料的重要性,在信息多元化的今天,我们不可能掌握所有的知识,但是面对的复杂的情况又要求我们掌握一些自己专业知识之外的内容,这时候就需要掌握正确的相关信息,学会求助书籍或者网络等是必不可少的。 查阅资料也使我们做的一切有理可依,而不是凭空想象。 通过这次课程设计我发现了自身存在的许多不足之处,自己感觉理论上已经掌握的东西,却不真正了解它的内涵和工作原理,需要经过一番努力才能解决存在的困惑。 这也很大地激发了我以后努力学习的兴趣。 通过这次设计,我懂得了学习的重要性,学会了坚持、耐心和努力,这将为自己以后的学习和工作做最好的铺垫。 参考文献 [1]AnthonyJones,JimOhlund.Windows网络编程技术.北京: 机械工业出版社,1999. [2]汪翔,袁辉.VisualC++实践
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- c+ 实现 ping 功能