计算机网络课程设计ping工具.docx
《计算机网络课程设计ping工具.docx》由会员分享,可在线阅读,更多相关《计算机网络课程设计ping工具.docx(21页珍藏版)》请在冰豆网上搜索。
计算机网络课程设计ping工具
摘要
关键词:
PING命令,IP/ICMP协议,SOCKET编程
摘要:
本课程设计包含了PING程序设计应用
其中PING程序设计使用了RAW模式的SOCKET编程,程序使用ICMP的封装机制,通过IP协议来工作。
本设计包括了具体设计任务,基本思路及所涉及的相关理论,设计流程图,调试过程中出现的问题及相应解决办法,实验运行结果,核心程序,个人体会及建议等。
概要设计
1.1题目的内容与要求
内容:
写一个程序,取代Windows下的Ping命令,可以Ping指定主机、一批主机,并以图形显示输出结果。
1.2总体结构
本程序主要分为四个模块(功能模块图见图1.1):
初始化模块,功能控制模块,数据报解读模块,Ping测试模块。
图1.1功能模块图
初始化模块:
该模块用于定义及初始化各个全局变量,为winsock加载winsock体。
功能控制模块:
该模块是被其他模块调用,其功能包括解析参数、计算ICMP数据报文检验和、清除SOCKET,ICMP包数据以及接受缓冲区。
数据报解读模块:
数据报解析模块提供了解读IP选项和解读ICMP报文的功能。
Ping测试模块:
该模块是本程序的核心模块,调用其他模块实现其功能,进而实现Ping的功能。
详细设计
2.1Ping主模块
Ping()函数是本程序的核心部分,它基本是调用其他模块的函数来实现最终功能,其主要布骤包括:
定义及初始化各个全局变量、打开socket动态库、设置接收和发送超时值、域名地址解析、分配内存、创建及初始化ICMP报文、发送ICMP请求报文、接收ICMP应答报文以及解读应答报文和输出Ping结果,最后释放占用的资源其流程如图所示。
先主要考虑实现ping单一主机的情况,当ping同一网段内的多台主机时只须循环调用ping单一主机时的情况即可
图2.1主模块流程图
2.2功能控制模块
功能控制模块主要是为其他模块提供可调用的函数,该模块主要包括参数获取功能、计算ICMP数据报文检验和、清除SOCKET,ICMP包数据以及接受缓冲区、占用资源释放功能和显示用尸帮助功能。
该模块一共包含三个函数来实现。
,流程如图2.2所示。
图2.2功能控制模块
1.2基本思路及所涉及的相关理论
1.2.1、设计基本思路
在PING的工作原理下,PING程序基于ICMP,使用ICMP的回送请求和回送应答来工作。
为了实现直接对IP和ICMP包进行操作,设计采用RAW模式的SOCKET编程,实现网络连通性的测试,探测主机到主机之间是否可通信,如果不能ping到某台主机,表明不能和这台主机建立连接。
当传送IP数据包发生错误,ICMP协议将会把错误信息封包,然后传送回给主机。
PING程序是面向用户的应用程序,该程序使用ICMP的封装机制,通过IP协议来工作。
1.2.2、PING的工作原理
PING程序是用来探测主机到主机之间是否可通信,如果不能ping到某台主机,表明不能和这台主机建立连接。
ping使用的是ICMP协议,它发送ICMP回送请求消息给目的主机。
ICMP协议规定:
目的主机必须返回ICMP回送应答消息给源主机。
如果源主机在一定时间内收到应答,则认为主机可达。
ICMP协议通过IP协议发送的,IP协议是一种无连接的,不可靠的数据包协议。
因此,保证数据送达的工作应该由其他的模块来完成。
其中一个重要的模块就是ICMP(网络控制报文)协议。
当传送IP数据包发生错误--比如主机不可达,路由不可达等等,ICMP协议将会把错误信息封包,然后传送回给主机。
给主机一个处理错误的机会,这也就是为什么说建立在IP层以上的协议是可能做到安全的原因。
ICMP数据包由8bit的错误类型和8bit的代码和16bit的校验和组成。
而前16bit就组成了ICMP所要传递的信息。
PING利用ICMP协议包来侦测另一个主机是否可达。
原理是用类型码为0的ICMP发请求,受到请求的主机则用类型码为8的ICMP回应。
ping程序来计算间隔时间,并计算有多少个包被送达。
用户就可以判断网络大致的情况。
1.2.3、RAW模式的SOCKET编程
WindowsSockets规范以U.C.Berkeley大学BSDUNIX中流行的Socket接口为范例定义了一套MicorosoftWindows下网络编程接口。
它不仅包含了人们所熟悉的BerkeleySocket风格的库函数;也包含了一组针对Windows的扩展库函数,以使程序员能充分地利用Windows消息驱动机制进行编程。
WindowsSockets规范本意在于提供给应用程序开发者一套简单的API,并让各家网络软件供应商共同遵守。
此外,在一个特定版本Windows的基础上,WindowsSockets也定义了一个二进制接口(ABI),以此来保证应用WindowsSocketsAPI的应用程序能够在任何网络软件供应商的符合WindowsSockets协议的实现上工作。
因此这份规范定义了应用程序开发者能够使用,并且网络软件供应商能够实现的一套库函数调用和相关语义。
任何能够与WindowsSockets兼容实现协同工作的应用程序就被认为是具有WindowsSockets接口。
我们称这种应用程序为WindowsSockets应用程序。
WindowsSockets规范定义并记录了如何使用API与Internet协议族(IPS,通常我们指的是TCP/IP)连接,尤其要指出的是所有的WindowsSockets实现都支持流套接口和数据报套接口.
应用程序调用WindowsSockets的API实现相互之间的通讯。
WindowsSockets又利用下层的网络通讯协议功能和操作系统调用实现实际的通讯工作。
程序详细设计说明
<1>初始化winsock2网络环境
WSADATAwsa;
if(WSAStartup(MAKEWORD(2,2),&wsa)!
=0)
{
cerr<<"\n初始化WinSock2DLL失败\n"
<<"errorcode:
"<return-1;
}
<2>//将命令行参数转换为IP地址
u_longulDestIP=inet_addr(argv[2]);
if(ulDestIP==INADDR_NONE)
{
//转换不成功时按域名解析
hostent*pHostent=gethostbyname(argv[2]);
if(pHostent)
{
ulDestIP=(*(in_addr*)pHostent->h_addr).s_addr;
//输出屏幕信息
cout<<"\nping"<}
else//解析主机名失败
{
cerr<<"\n解析主机名"<"<<'\n'
<<"errorcode:
"<WSACleanup();
return-1;
}
}
else
{
//输出屏幕信息
cout<<"ping主机"<}
<3>填充目的Socket地址
sockaddr_indestSockAddr;
ZeroMemory(&destSockAddr,sizeof(sockaddr_in));
destSockAddr.sin_family=AF_INET;
destSockAddr.sin_addr.s_addr=ulDestIP;
<4>创建RawSocket
SOCKETsockRaw=WSASocket(AF_INET,SOCK_RAW,IPPROTO_ICMP,NULL,0,WSA_FLAG_OVERLAPPED);
if(sockRaw==INVALID_SOCKET)
{
cerr<<"\n创建rawsocket失败\n";
WSACleanup();
return-1;
}
<5>设置端口属性
intiTimeout=DEF_ICMP_TIMEOUT;
if(setsockopt(sockRaw,SOL_SOCKET,SO_RCVTIMEO,(char*)&iTimeout,sizeof(iTimeout))==SOCKET_ERROR)
{
cerr<<"\n设置发送时间超时失败\n";
closesocket(sockRaw);
WSACleanup();
return-1;
}
if(setsockopt(sockRaw,SOL_SOCKET,SO_SNDTIMEO,(char*)&iTimeout,sizeof(iTimeout))==SOCKET_ERROR)
{
cerr<<"\n设置接收时间超时失败\n";
closesocket(sockRaw);
WSACleanup();
return-1;
}
<6>创建ICPM包发送缓冲区和接收缓冲区
charIcmpSendBuf[sizeof(ICMP_HEADER)+DEF_ICMP_DATA_SIZE];
memset(IcmpSendBuf,0,sizeof(IcmpSendBuf));
charIcmpRecvBuf[MAX_ICMP_PACKET_SIZE];
memset(IcmpRecvBuf,0,sizeof(IcmpRecvBuf));
<7>填充待发送的ICMP包
ICMP_HEADER*pIcmpHeader=(ICMP_HEADER*)IcmpSendBuf;pIcmpHeader->type=ICMP_ECHO_REQUEST;//类型为请求回显pIcmpHeader->code=0;//代码字段为0
pIcmpHeader->id=(USHORT)GetCurrentProcessId();//ID字段为当前进程号
USHORTusSeqNo=0;
//以默认大小填充数据字段
memset(IcmpSendBuf+sizeof(ICMP_HEADER),'E',DEF_ICMP_DATA_SIZE);
<7>发送ICMP数据报
if(sendto(sockRaw,IcmpSendBuf,sizeof(IcmpSendBuf),0,
(sockaddr*)&destSockAddr,sizeof(destSockAddr)==SOCKET_ERROR
{
//如果目的主机不可达则直接退出
if(WSAGetLastError()==WSAEHOSTUNREACH)
cout<<"发送失败:
"<closesocket(sockRaw);
WSACleanup();
return0;
}
<8>接收ICMP数据报
while
(1)
{
iReadDataLen=recvfrom(sockRaw,IcmpRecvBuf,MAX_ICMP_PACKET_SIZE,0,(sockaddr*)&from,&iFromLen);
if(iReadDataLen!
=SOCKET_ERROR)//有数据包到达
{
if(DecodeIcmpResponse(IcmpRecvBuf,iReadDataLen,
stDecodeResult,ittl))
{
if(stDecodeResult.dwIPaddr.s_addr==destSockAddr.sin_addr.s_addr)
{
Recived++;
cout<<"Replyfrom"<stDecodeResult.dwRoundTripTime=GetTickCount()-stDecodeResult.dwRoundTripTime;
//统计传输时间
SumTripTime+=stDecodeResult.dwRoundTripTime;
if(Recived==1){MaxTripTime=MinTripTime=stDecodeResult.dwRoundTripTime;}
else
{
if(stDecodeResult.dwRoundTripTime>MaxTripTime)MaxTripTime=stDecodeResult.dwRoundTripTime;
if(stDecodeResult.dwRoundTripTime}
//输出本次传输时间
if(stDecodeResult.dwRoundTripTime)
cout<<":
time="<else
cout<<":
time"<<"<1"<<"ms";
cout<<"TTl="<break;
}
else
{
cout<<"Requesttimedout.";
}
}
}
elseif(WSAGetLastError()==WSAETIMEDOUT)//接收超时
{
cout<<"Requesttimedout."<break;
}
else
{
cerr<<"\n接收数据失败!
\n"
<<"errorcode:
"<closesocket(sockRaw);
WSACleanup();
return-1;
}
<9>定义头类型文件
#include"StdAfx.h"
typedefstruct
{
unsignedcharhdr_len:
4;//lengthoftheheader
unsignedcharversion:
4;//versionofIP
unsignedchartos;//typeofservice
unsignedshorttotal_len;//totallengthofthepacket
unsignedshortidentifier;//uniqueidentifier
unsignedshortfrag_and_flags;//flags
unsignedcharttl;//timetolive
unsignedcharprotocol;//protocol(TCP,UDPetc)
unsignedshortchecksum;//IPchecksum
unsignedlongsourceIP;//sourceIPaddress
unsignedlongdestIP;//destinationIPaddress
}IP_HEADER;
//ICMP数据报头
typedefstruct
{
BYTEtype;//8位类型
BYTEcode;//8位代码
USHORTcksum;//16位校验和
USHORTid;//16位标识符
USHORTseq;//16位序列号
}ICMP_HEADER;
//解码结果
typedefstruct
{
USHORTusSeqNo;//包序列号
DWORDdwRoundTripTime;//往返时间
in_addrdwIPaddr;//对端IP地址
}DECODE_RESULT;
//线程参数
structThread_Param
{
charIPaddr1[20];
charIPaddr2[20];
};
typedefstructThread_ParamThread_Param;
typedefstructThread_Param*pThread_Param;
//ICMP类型字段
constBYTEICMP_ECHO_REQUEST=8;//请求回显
constBYTEICMP_ECHO_REPLY=0;//回显应答
constBYTEICMP_TIMEOUT=11;//传输超时
constDWORDDEF_ICMP_TIMEOUT=50;//默认超时时间,单位ms
constintDEF_ICMP_DATA_SIZE=32;//默认ICMP数据部分长度
constintMAX_ICMP_PACKET_SIZE=1024;//最大ICMP数据报的大小
constintDEF_MAX_HOP=30;//最大跳站数
USHORTGenerateChecksum(USHORT*pBuf,intiSize);
BOOLDecodeIcmpResponse(char*pBuf,intiPacketSize,DECODE_RESULT&stDecodeResult,unsignedchar&ittl);
DWORDSingle_ThreadProc(LPVOIDlpParameter);
DWORDMulti_ThreadProc(LPVOIDlpParameter);
classPing
{
public:
Ping();
~Ping();
voidAction_Clear();
voidSetMainHwnd(HWNDhwnd);
voidStart();
voidScanSingleHost(char*IPaddr1,char*IPaddr2);
voidScanMultiHost(char*IPaddr1,char*IPaddr2);
private:
HWNDmain_hwnd;
protected:
};
<10>输出窗口图形化设计
StatusWindow:
:
StatusWindow()
{
nID=0;
num_parts=1;
hStatusWin=NULL;
}
BOOLStatusWindow:
:
CreateStatus(HWNDhwnd,intnum_parts,int*Width,intnID)
{
this->nID=nID;
if(Width==NULL)
{
MessageBox(hwnd,TEXT("宽度数组不能为空!
"),TEXT("提示"),MB_ICONWARNING);
returnFALSE;
}
if(num_parts>=1&&num_parts<=255)
{
this->num_parts=num_parts;
}
else
{
MessageBox(hwnd,TEXT("分栏数范围(1-255)!
"),TEXT("提示"),MB_ICONWARNING);
returnFALSE;
}
hStatusWin=CreateStatusWindow(WS_CHILD|WS_VISIBLE,NULL,hwnd,nID);//IDC_STATUS需要在resource.h里面自己定义
if(hStatusWin==NULL)
{
MessageBox(hwnd,TEXT("创建状态栏失败!
"),TEXT("提示"),MB_ICONERROR);
returnFALSE;
}
BOOLresult=SendMessage(hStatusWin,SB_SETPARTS,(WPARAM)num_parts,(LPARAM)Width);
if(result==FALSE)
{
MessageBox(hwnd,TEXT("设置分栏操作失败!
"),TEXT("提示"),MB_ICONERROR);
returnresult;
}
returnTRUE;
}
StatusWindow:
:
~StatusWindow()
{
}
BOOLStatusWindow:
:
InsertItem(HWNDhwnd,intIndex_part,char*contents)
{
if(Index_part<0||Index_part>num_parts)
{
MessageBox(hwnd,TEXT("状态栏索引错误!
"),TEXT("提示"),MB_ICONWARNING);
returnFALSE;
}
if(contents==NULL)
{
MessageBox(hwnd,TEXT("内容不能为空!
"),TEXT("提示"),MB_ICONWARNING);
returnFALSE;
}
if(hStatusWin==NULL)
{
MessageBox(hwnd,TEXT("状态栏句柄为空!
"),TEXT("提示"),MB_ICONWARNING);
returnFALSE;
}
BOOLret=SendMessage(hStatusWin,SB_SETTEXT,(WPARAM)Index_part,(LPARAM)contents);
if(ret==FALSE)
{
MessageBox(hwnd,TEXT("状态栏插入文本操作失败!
"),TEXT("提示"),MB_ICONWARNING);
returnret;
}
returnTRUE;
}
HWNDStatusWindow:
:
GethStatus()
{
returnhStatusWin;
}
使用说明结果分析
在VC中运行程序后会出现如下图所示,提示你输入IP
当要pin