课程设计课程名称____计算机网络_____题目名称_Ping程序设计与实现学生学院____计算机学院____专业班级____08计科4班___学号_______2011年 1 月8 日一、课程设计目的本次课程设计的目的旨在通过网络相关程序的编写,使我们能深入理解TCP/IP协议机制,以及初步掌握基于Winsocket的网络编程技术。
同时巩固相关的理论知识,为以后程序开发打下良好而又坚实的基础。
二、知识准备1、ICMP协议互联网控制信息协议(Internet CONTROL Message Protocol),用于错误报告和调试。
该协议是TCP/IP协议集中的一个子协议,属于网络层协议,主要用于在主机与路由器之间传递控制信息,包括报告错误、交换受限控制和状态信息等。
当遇到IP数据无法访问目标、IP路由器无法按当前的传输速率转发数据包等情况时,会自动发送ICMP消息。
我们可以通过Ping命令发送ICMP回应请求消息并记录收到ICMP回应回复消息,通过这些消息来对网络或主机的故障提供参考依据。
常见ICMP报文有响应请求报文,目标不可到达、源抑制和超时报文,此外还有时间戳报文。
2、Ping工作原理Ping的原理就是首先建立通道,然后发送包,对方接受后返回信息,这个包至少包括以下内容:发送的时候,包的内容包括对方的ip地址和自己的地址,还有序列数;回送的时候包括双方地址,还有时间等,主要是接受方在都是在操作系统内核里做好的,时刻在监听。
Ping程序生成一个icmp“回送请求”,将其发送给目的主机。
通过检测是否可以收到目标主机的应答,便可以知道网络的连通性。
(3) Tracert 工作原理透过向目标发送不同 IP 生存时间 (TTL) 值的ICMP回应数据包,Tracert 诊断程序确定到目标所采取的路由。
要求路径上的每个路由器在转发数据包之前至少将数据包上的 TTL 递减 1。
数据包上的 TTL 减为 0 时,路由器应该将“ICMP已超时”的消息发回源系统。
Tracert 先发送 TTL 为 1 的响应数据包,并在随后的每次发送过程将 TTL 递增1,直到目标响应或 TTL 达到最大值,从而确定路由。
透过检查中间路由器发回的“ICMP已超时”的消息确定路由。
三、界面设计及数据结构的说明1 、MFC界面设计建立流程步骤一:打开VC6.0,点击文件选项,在project选项卡中选择MFC Appwizard(exe),项目名称为ping。
步骤二:在新窗口中选择窗口类型为基于对话框的类型。
步骤三:设置界面选项步骤四:设置窗口类型步骤五:选择相应类,点击finish完成。
界面建立成功后添加相应按钮及功能模块。
主界面设计如下:2、数据结构的定义及说明(1)定义IP头部typedef struct _IpHeader{unsigned int HeaderLength:4; // 头部长度unsigned int Version:4; // IP版本号unsigned char TypeOfService; // 服务类型unsigned short TotalLength; // 收发包的总长度unsigned short Identification; // 唯一校验符unsigned short FragmentationFlags; // 标志位unsigned char TTL; // 生存时间unsigned char Protocol; // 协议类型 (TCP, UDP 等)unsigned short CheckSum; // IP头部校验和unsigned int sourceIPAddress; // 源地址unsigned int destIPAddress; // 目的地址} IpHeader;(2)定义ICMP头部typedef struct _IcmpHeader{BYTE IcmpType; //icmp类型BYTE IcmpCode; //编码类型USHORT IcmpChecksum; //icmp校验和USHORT IcmpId;USHORT IcmpSeq; //icmp序列号ULONG IcmpTimestamp; // 头部无标准定义,但仍需保留} IcmpHeader;(3)CIcmp类class CIcmp : public CSocket{public:BOOL OpenNewSocket(HWND hWnd, unsigned int NotificationMessage, long NotifyEvents);BOOL OpenNewSocket(HWND hWnd, unsigned int NotificationMessage, long NotifyEvents, int AFamily, int AType, int AProtocol);int CloseIcmpSocket(void);BOOL Connect(int ReceiveTimeout, int SendTimeout);BOOL Connect(LPINT ReceiveTimeout, LPINT SendTimeout, int AFamily, int AType, int AProtocol);int SetTTL(int TTL);int SetAsynchNotification(HWND hWnd, unsigned int Message, long Events);int Receive(LPSTR pIcmpBuffer, int IcmpBufferSize);unsigned long GetIPAddress (LPSTR iHostName);int Ping (LPSTR pIcmpBuffer, int IcmpBufferSize);unsigned short IcmpChecksum(unsigned short FAR *lpBuf, int Len);void DisplayError(CString ErrorType, CString FunctionName);public:CIcmp(void);CIcmp(CIcmp ©);~CIcmp(void);private:public:LPIcmpHeader pIcmpHeader;LPIpHeader pIpHeader;SOCKET icmpSocket;SOCKADDR_IN icmpSockAddr;SOCKADDR_IN rcvSockAddr;DWORD icmpRoundTripTime;DWORD icmpPingSentAt;DWORD icmpPingReceivedAt;int icmpRcvLen;int icmpHops;int icmpMaxHops;int icmpCurSeq;int icmpCurId;int icmpPingTimer;int icmpSocketError;int icmpSocketErrorMod;unsigned long icmpHostAddress;protected:};typedef CIcmp FAR * LPIcmp;(4)CPingPlusDlg类class CPingPlusDlg : public CDialog{enum ImageType{Icon_Blank,Icon_BlueArrow};private:void StopTimer();void StartTimer();void ChangeIconState(void);BOOL InitSockets();BOOL FetchWinsockSettings();BOOL LoadRegValues(void);void SaveRegValues();void SendPing(void);unsigned long HostIPAddress();void UpdateTrace ();void DisplayTrace(LPCSTR TripTimeMessage, LPCSTR IPAddressMessage, LPCSTR HostMessage); void EndTrace(void);void InitImageList();int AddListColumn(int column, int lfmt, int lmaxwidth, LPSTR ltext, int lsubitem);void DisplayBlankLine(void);void SetTraceSequence(int Seq, int FocusItem, ImageType FocusImage);void SetTraceFocus(int FocusItem, int FocusSubItem);void SetDisplayImage(int FocusItem, enum ImageType FocusImage);void TraceComment(CString Comment);public:CPingPlusDlg(CWnd* pParent = NULL); // 标准构造器protected:HICON m_hIcon;LRESULT OnPINGAsynch(WPARAM wParam, LPARAM lParam);public:CIcmp PingSocket; // Icmp 对象WSADATA wsaData; // Windows Socket 信息结构CString LocalNameServer;CString LocalDomainName; // 域名 (从注册机构获取)CString DefHost; // 默认主机名 (从注册机构获取)char HostName[MAXHOSTNAME]; // 将主机名加入到会话中去char CurrentHostName[MAXHOSTNAME]; // 当前正在ping的主机struct in_addr TraceTarget; // 操作路径终端BOOL TimerActive; // TRUE = 计时器到时unsigned long icmpIntervalCount; // 当前计时时间间距unsigned long icmpMaxIntervals; // 最大间距char icmpBuffer[MAX_PACKET]; // icmp操作的普通缓冲区LPSTR pIcmpBuffer; // icmp缓冲区的故障分析报告指向int icmpDataLen; // icmp发出信息的字节数 (不计算头部)int icmpPingTTL; // 路径操作所用到的ttl值BOOL icmpTracing;// TRUE = 路径操作, FALSE = PING操作BOOL PingSent; // TRUE = 等待ECHO回复, FALSE = 忽略输入};四、程序主题功能设计模块1、调用ping模块void CPingPlusDlg::OnPingButton(){IconState = 0;DisplayBlankLine ();icmpTracing = FALSE;icmpPingTTL = PingSocket.icmpMaxHops; //ping生存时间为最大icmp跳数PingSocket.icmpCurSeq = 0;//当前icmp序列为0SendPing ( );//开始执行ping}2、初始化socketBOOL CPingPlusDlg::InitSockets(){if (!PingSocket.OpenNewSocket(GetSafeHwnd(),WSA_PING_ASYNC,FD_READ | FD_WRITE,AF_INET,SOCK_RAW,IPPROTO_ICMP)){PingSocket.DisplayError ("WSA_PING_ASYNC","CPingPlusDlg::InitSockets");return FALSE;}return TRUE;}3、获取winsocket设置信息BOOL CPingPlusDlg::FetchWinsockSettings(){SysTCPIP STcpIp;if (!STcpIp.WinsockVersion(&wsaData)){MessageBox ("No valid winsock.dll detected","CPingPlusDlg::OnInitDialog",MB_OK|MB_SYSTEMMODAL);return FALSE;}if (!STcpIp.GetLocalHostName (&m_LocalHost)){gethostname(CurrentHostName, MAXHOSTNAME);}else{memcpy (CurrentHostName, m_LocalHost, m_LocalHost.GetLength());CurrentHostName[m_LocalHost.GetLength()] = 0;}m_LocalHost = CurrentHostName;if (!STcpIp.GetDomainName(&LocalDomainName))LocalDomainName = "";m_LocalHost += "." + LocalDomainName;memcpy (CurrentHostName, m_LocalHost, m_LocalHost.GetLength());CurrentHostName[m_LocalHost.GetLength()] = 0;if (!STcpIp.GetNSName (&LocalNameServer))LocalNameServer = "";m_NameServer = "Name Server: " + LocalNameServer;SetDlgItemText (IDC_LocalHost, m_LocalHost);SetDlgItemText (IDC_NameServer, m_NameServer); LoadRegValues();SetDlgItemText (IDC_DEST, HostName);return TRUE;}4、发送ping信息void CPingPlusDlg::SendPing(void){PingSent = TRUE;PingSocket.icmpCurSeq++;PingSocket.icmpCurId = (USHORT)GetCurrentProcessId(); PingSocket.icmpHostAddress = HostIPAddress();if (PingSocket.icmpHostAddress == NULL)return;if (icmpTracing){icmpPingTTL++; }if (PingSocket.SetTTL (icmpPingTTL) == SOCKET_ERROR) { PingSocket.DisplayError ("setsocket(TTL)","CPingPlusDlg::SendPing");return; }Sleep (100);// 用蓝色箭头显示序列号及相应信息SetTraceSequence (PingSocket.icmpCurSeq,m_TraceList.GetItemCount(),Icon_BlueArrow);PingSocket.icmpSockAddr.sin_family = PF_INET;PingSocket.icmpSockAddr.sin_addr.s_addr = PingSocket.icmpHostAddress;PingSocket.icmpSockAddr.sin_port = 0;StartTimer();if (PingSocket.Ping (pIcmpBuffer, icmpDataLen) == SOCKET_ERROR)PingSocket.DisplayError("Ping", "CPingPlusDlg::SendPing");}5、设置超时时间int CIcmp::SetTTL(int TTL){int Result;Result = setsockopt (icmpSocket, IPPROTO_IP, IP_TTL, (LPSTR)&TTL, sizeof(int)); if (Result == SOCKET_ERROR){icmpSocketErrorMod = 1;icmpSocketError = WSAGetLastError(); }return Result;}6、Ping函数int CIcmp::Ping (LPSTR pIcmpBuffer, int DataLen){int Result;int IcmpBufferSize = DataLen + IcmpHeaderLength;pIcmpHeader = (LPIcmpHeader)pIcmpBuffer;memset (pIcmpBuffer, 'E', IcmpBufferSize);memset (pIcmpHeader, 0, IcmpHeaderLength);pIcmpHeader->IcmpType = ICMP_ECHO;pIcmpHeader->IcmpCode = 0;pIcmpHeader->IcmpChecksum = 0;pIcmpHeader->IcmpId = icmpCurId;pIcmpHeader->IcmpSeq = icmpCurSeq;pIcmpHeader->IcmpTimestamp = GetCurrentTime();pIcmpHeader->IcmpChecksum = IcmpChecksum ((USHORT FAR *)pIcmpBuffer,IcmpBufferSize); icmpPingSentAt = GetCurrentTime();Result = sendto (icmpSocket,pIcmpBuffer,IcmpBufferSize,0,(LPSOCKADDR)&icmpSockAddr,sizeof icmpSockAddr);if (Result == SOCKET_ERROR){ icmpSocketError = WSAGetLastError();icmpSocketErrorMod = 1;}return Result;}7、校验和函数unsigned short CIcmp::IcmpChecksum(unsigned short FAR *lpBuf, int Len) {register long ChkSum = 0L;while (Len > 1){ ChkSum += *(lpBuf++);Len -= sizeof (USHORT); }if (Len)ChkSum += *(UCHAR *)lpBuf;ChkSum = (ChkSum & 0xffff) + (ChkSum>>16);ChkSum += (ChkSum >> 16);#pragma warning(disable : 4244)return (~ChkSum);#pragma warning(default : 4244)}8、Receive函数int CIcmp::Receive(LPSTR pIcmpBuffer, int IcmpBufferSize) {LPSOCKADDR pRcvSockAddr = (LPSOCKADDR)&rcvSockAddr; int Result;int RcvIpHdrLen;icmpPingReceivedAt = GetTickCount();icmpCurId = 0;rcvSockAddr.sin_family = AF_INET;rcvSockAddr.sin_addr.s_addr = INADDR_ANY;rcvSockAddr.sin_port = 0;RcvIpHdrLen = sizeof rcvSockAddr;Result = recvfrom (icmpSocket,pIcmpBuffer,IcmpBufferSize,0,pRcvSockAddr,&RcvIpHdrLen);if (Result == SOCKET_ERROR){ icmpSocketError = WSAGetLastError();icmpSocketErrorMod = 1;DisplayError ("Receive","CIcmp::Receive");return Result;}icmpRcvLen = Result;pIpHeader = (LPIpHeader)pIcmpBuffer;RcvIpHdrLen = pIpHeader->HeaderLength * 4;if (Result < RcvIpHdrLen + ICMP_MIN){MessageBox(NULL,"Short message!","CIcmp::Receive",MB_OK|MB_SYSTEMMODAL);icmpSocketErrorMod = 2;return Result;}pIcmpHeader = (LPIcmpHeader)(pIcmpBuffer + RcvIpHdrLen);icmpCurId = pIcmpHeader->IcmpId;icmpRoundTripTime = icmpPingReceivedAt - pIcmpHeader->IcmpTimestamp;if (pIcmpHeader->IcmpType != ICMP_ECHOREPLY){return Result;}icmpCurSeq = pIcmpHeader->IcmpSeq;return Result;}五、运行结果用ping命令测试本地主机的ip地址六、心得体会通过本次课程设计,我感觉到自己在编程方面,能力还十分欠缺。