计算机网络基础课程设计Ping程序实现

来源:职称计算机 发布时间:2021-04-29 点击:

计算机网络工程课程设计报告 题 目:
Ping程序的实现 学生姓名:
学 号:
专业班级:
计科专业班 同组姓名:   指导教师:
设计时间:
2013年下学期第X周 指导老师意见:
评定成绩:
签名:
日期:
年 月 日 目录 一、课程设计的目的和意义 2 二、课程设计的内容和要求 2 1.内容:
2 2.要求:
2 三、课程设计的相关技术 2 四、课程设计过程 2 1.Ping主模块 2 2.功能控制模块 4 3.数据报解析模块 5 五、课程设计小结 6 六、参考文献 7 七、附 录(程序清单)
7 八、心得体会 11 一、课程设计的目的和意义 利用ICMP数据包、C语言实现Ping命令程序,能实现基本的Ping操作,发送ICMP回显请求报文,用于测试—个主机到只一个主机之间的连通情况。通过本程序的训练,使学生熟悉ICMP报文结构,使学生对ICMP有更深的理解,掌握Ping程序的设计方法,掌握网络编程的方法和技巧,从而编写出功能更强大的程序。

二、课程设计的内容和要求 1.内容:
用C语言实现Ping命令程序,能实现基本的Ping操作,发送ICMP回显请求报文,用于测试—个主机到只一个主机之间的连通情况。

2.要求:
1) 独立完成程序的设计、编码和调试。

2) 系统利用C语言实现,程序调试环境为Turbo C或VC;

3) 按照课程设计规范书写课程设计报告。

4) 采用VC环境进行调试运行。

三、课程设计的相关技术 由于Ping程序是面向用户的应用程序,该程序使用ICMP的封装机制,通过IP协议来工作。为了实现直接对IP和ICMP包进行操作,实验中使用RAW模式的socket编程。首先定义IP数据报首部,在IP数据报的基础上定义ICMP数据报首部,并初始化一些全局变量。接着自定义填充ICMP数据报字段函数FillICMPData()、校验和函数checksum()、解读ICMP报首部函数DecodeICMPHeader()、释放资源函Cleanup()。最后主函数通过调用这些函数来实现Ping命令功能。IP头与ICMP头的设置分别参照RFC791及RFC792的标准,包含所有必要信息。主程序设置main()函数,主函数用库函数实现套接字编程用于数据包发送及接收,其中,数据包发送调用sendto(),数据包接收调用recvfrom( ),由于发送数据包时可能会遇到阻塞或者目标主机不通,造成超时,因此需要在发送数据包后调用一个函数判断是否超时,此处调用库函数setsockopt()来实现超时判断;
其次,校验和函数采用移位方法进行计算。

套接字所需要的文件有头文件Winsocket2.h、库文件WS2_32.LIB、动态库W32_32.DLL。创建套接字的时候参数的以及在创建套接字之前必须首先使用WSAStartup函数。

四、课程设计过程 1.Ping主模块 Ping()函数是本程序的核心部分,它基本是调用其他模块的函数来实现最终功能,其主要布骤包括:定义及初始化各个全局变量、打开socket动态库、设置接收和发送超时值、域名地址解析、分配内存、创建及初始化ICMP报文、发送ICMP请求报文、接收ICMP 应答报文以及解读应答报文和输出Ping结果,最后释放占用的资源其流程如下页图2.1所示。

注释:
(1)
该模块并非只有处理还包括判断及输出判断结果的含义;

(2)
程序没运行一次就只能输出四行结果(前提是输入的地址有效),欲再次PING其他地址必须要重新启动程序。

(3)
输入时不能输入目标主机名,不然ping结果为TIMEOUT; 开始 定义及初始化各个全局变量 判断WSAStartup函数是否调用成功 输出调用失败 否 创建套接字以及设置socket接收超时,发送超时选项;

是 输入PING的IP地址 解析输入内容,设置PING参数 创建及填充ICMP数据报文 判断是否已发送四次 Break; 发送,接收以及解析数据包 输出PIING结果 是 结束 清除残余 否 2.功能控制模块 功能控制模块主要是为其他模块提供可调用的函数,该模块主要包括参数获取功能、计算ICMP数据报文检验和、清除SOCKET,ICMP包数据以及接受缓冲区、占用资源释放功能和显示用尸帮助功能。该模块一共包含三个函数来实现。,流程如图2.2所示。

Checksum开始 定义初始化cksum (size > 1) 确定cksum及size大小 是 if (size) 计算校验cksum,获得结果 cksum += *(UCHAR*)buffer; 否 结束 Cleanup开始 if (m_hSocket != INVALID_SOCKET) 关闭套接字 释放占用资源 清除ICMP包数据以及接受缓冲区 F WSACleanup(); 结束 图2.2 功能控制模块 注释:
a.illICMPData是由一系列的初始化的语句在流程图中不再画出;

b.Cleanup()函数中的WSACleanup(),HeapFree(),closesocket()都是一些库函数。checksum()校验和函数是冗余校验的一种形式。

它是通过错误检测方法,对经过空间(如通信)或者时间(如计算机存储)传送的数据的完整性进行检查的一种简单方法。

3.数据报解析模块 数据报解析模块提供了解读IP选项和解读IcMP报文的功能。从本机收到目的主机返回的1cMP回显应答报文,就开始逐个地解读IcMP报文,如果需要记录路由的情况下,IcMP解析函数将调用IP选项解读函数来实现IP路由的输出(但本程序没有此功能。该模块主要由DecodeICMPHeader一个函数来实现,而中间也会调用其它模块的相应函数。其流程图如图2.3: 注释:
a.判断是否为我们所要的数据报回应之前,还有一些判断回应多少内容的语句未呈现出;

b.函数GetTickCount()是用来记录此时我机所处的现在时间(毫秒级);

DecodeICMPHeader开始 定义相关变量以及初始化;

tick = GetTickCount(); 为我们所要的回应报文;

是 输出不是我们所要;

tick0[icmpcount]=tick - icmphdr->timestamp; 判断时间是否小于1ms printf(“Reply from %s: dytes=%d time<1ms icmp_seq = %d\n“,inet_ntoa(from->sin_addr), bytes, icmphdr->i_seq ); printf(“Reply from %s: dytes=%d time=%d icmp_seq = %d\n“,inet_ntoa(from->sin_addr), bytes,tick0[icmpcount], icmphdr->i_seq); 是 是 icmpcount++; 结束 结束 否 图2.3 数据报解析模块 五、课程设计小结 1.运行操作结果:在vc里运行之后界面:
2.输入本机ip地址:
3.输入网上ip看结果:
4. 但是当网络连不通时,就会出现下图结果 六、参考文献 【1】Visual C++网络通信编程实用案例精选(第二版)曹衍龙 刘海英 编著;

【2】Windows网络编程技术 (美);

七、附 录(程序清单)
#pragma comment(lib,“ws2_32.lib“) #include <winsock2.h>//创建套接字头文件 #include <ws2tcpip.h> #include <stdio.h>//标准输入输出函数 #include <stdlib.h>//实用程序库函数 #include <string.h> typedef struct iphdr { unsigned int h_len:4; // 头长度 unsigned int version:4; // IP版本 unsigned char service; // 服务类型 unsigned short total_len; // 包的总长度 unsigned short ident; // 包标示身份 unsigned short frag_and_flags; // 标志 unsigned char ttl; // 包生命周期 unsigned char proto; // 协议类型 unsigned short checksum; // IP 校验 unsigned int sourceIP; //源IP unsigned int destIP; //目标IP } IpHeader; #define ICMP_ECHO 8 //ICMP报文类型,回显请求 #define ICMP_ECHOREPLY 0 //ICMP报文类型,回显应答 #define ICMP_MIN 8 //最小的ICMP数据报大小 typedef struct icmphdr { BYTE i_type; //ICMP报文类型 BYTE i_code; //该类型中的代码号 USHORT i_cksum; //校验和 USHORT i_id; //惟一的标识符 USHORT i_seq; //序列号 ULONG timestamp; //时间戳 } IcmpHeader; #define DEF_PACKET_SIZE 32 //默认数据报大小 #define MAX_PACKET 1024 // 最大的ICMP数据报大小 #define MAX_IP_HDR_SIZE 60 // 最大IP头长度 //初始化全局变量 int datasize=DEF_PACKET_SIZE; char *icmp_data=NULL; char *recvbuf=NULL; SOCKET m_hSocket= INVALID_SOCKET; char *lpdest=NULL; //填充ICMP数据报字段函数 void FillICMPData(char *icmp_data, int datasize) { IcmpHeader *icmp_hdr = NULL; char*datapart = NULL; icmp_hdr = (IcmpHeader*)icmp_data; icmp_hdr->i_type = ICMP_ECHO; icmp_hdr->i_code = 0; icmp_hdr->i_id = (USHORT)GetCurrentProcessId(); icmp_hdr->i_cksum = 0; icmp_hdr->i_seq = 0; datapart = icmp_data + sizeof(IcmpHeader); } //校验和函数 USHORT checksum(USHORT *buffer, int size) { unsigned long cksum=0; while (size > 1) { cksum += *buffer++; size -= sizeof(USHORT); } if (size) { cksum += *(UCHAR*)buffer; } cksum = (cksum >> 16) + (cksum & 0xffff); cksum += (cksum >>16);return (USHORT)(~cksum); } //解读ICMP报首部函数 void DecodeICMPHeader(char *buf, int bytes, SOCKADDR_IN *from) { IpHeader*iphdr = NULL; IcmpHeader*icmphdr = NULL; unsigned short iphdrlen; DWORD tick; static int icmpcount = 0; iphdr = (IpHeader *)buf; iphdrlen = iphdr->h_len * 4; tick = GetTickCount(); if (bytes < iphdrlen + ICMP_MIN) { printf(“Too few bytes from %s \r\n“,inet_ntoa(from->sin_addr)); } icmphdr = (IcmpHeader*)(buf + iphdrlen); if (icmphdr->i_type != ICMP_ECHOREPLY) { printf(“nonecho type %d received \r\n“, icmphdr->i_type); } if (icmphdr->i_id != (USHORT)GetCurrentProcessId()) { printf(“其他程序的回应报文! \t错误代码 %d\n“, WSAGetLastError()); } DWORD tick0[4]; tick0[icmpcount]=tick - icmphdr->timestamp; if(tick0[icmpcount]<1) printf(“Reply from %s: bytes=%d time<1ms icmp_seq=%d\n“,inet_ntoa(from->sin_addr), bytes, icmphdr->i_seq ); else printf(“Reply from %s: bytes=%d time=%dms icmp_seq = %d\n“,inet_ntoa(from->sin_addr), bytes,tick0[icmpcount], icmphdr->i_seq); icmpcount++; } //释放资源函数 void Cleanup() { if (m_hSocket != INVALID_SOCKET) closesocket(m_hSocket); HeapFree(GetProcessHeap(), 0, recvbuf); HeapFree(GetProcessHeap(), 0, icmp_data); WSACleanup(); } //主函数 void main() { WSADATA wsaData; char a[100]; printf(“ping “); scanf(“%s“,a); lpdest=a; SOCKADDR_IN m_addrDest;//结构体 SOCKADDR_IN m_addrFrom; int timeout=1000; USHORT seq_no=0; if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) { printf(“Sorry, you cannot load socket dll!“); } m_hSocket = WSASocket (AF_INET, SOCK_RAW, IPPROTO_ICMP, NULL, 0,WSA_FLAG_OVERLAPPED);//创建原始套接字,该套接字用于ICMP协议 if (m_hSocket == INVALID_SOCKET) //如果套接字创建不成功 { printf(“socket 创建失败!“); } int bread = setsockopt(m_hSocket, SOL_SOCKET, SO_RCVTIMEO, (char*)&timeout, sizeof(timeout));//设置接收的超时值 if(bread == SOCKET_ERROR) { printf(“设置socket接收超时选项错误!“); } timeout = 1000; bread = setsockopt(m_hSocket, SOL_SOCKET, SO_SNDTIMEO, (char*)&timeout, sizeof(timeout));//设置发送的超时值 if (bread == SOCKET_ERROR) { printf(“设置socket发送超时选项错误!“); } memset(&m_addrDest, 0, sizeof(m_addrDest));// 用0初始化目的地地址 m_addrDest.sin_family = AF_INET;//设置地址族,这里表示使用IP地址族 if ((m_addrDest.sin_addr.s_addr = inet_addr(lpdest)) == INADDR_NONE)//地址转化 { struct hostent *hp = NULL; if ((hp = gethostbyname(lpdest)) != NULL) //名字解析,根据主机名获取IP地址 { memcpy(&(m_addrDest.sin_addr), hp->h_addr, hp->h_length);//将获取到的IP值赋给目的地地址中的相应字段 m_addrDest.sin_family = hp->h_addrtype; //将获取到的地址族值赋给目的地地址中的相应字段 } else { printf(“不能找到名为 %s 的主机\t错误代码 %d\n“,lpdest, exit(0); } } printf(“Pinging %s with 64 bytes of data: \n\n“, inet_ntoa(m_addrDest.sin_addr)); datasize += sizeof(IcmpHeader); //数据报文大小需要包含ICMP报头 //根据默认堆句柄,从堆中分配MAX_PACKET内存块,新分配内存的内容将被初始化为0 icmp_data=(char*)HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,MAX_PACKET); recvbuf =(char*) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,MAX_PACKET); if (!icmp_data) //如果分配内存不成功 { printf(“堆分配错误!“); } memset(icmp_data,0,MAX_PACKET);//创建ICMP报文 FillICMPData(icmp_data,datasize); // 开始发送或接受ICMP包 int nCount=0; while(1) { int bwrote; if(nCount++ == 4) break;//超过指定的记录条数则退出 ((IcmpHeader*)icmp_data)->i_cksum = 0;//计算校验和前要把校验和字段设置为0 ((IcmpHeader*)icmp_data)->timestamp = GetTickCount();//获取操作系统启动到现在所经过的毫秒数,设置时间戳 ((IcmpHeader*)icmp_data)->i_seq = seq_no++;//设置序列号 ((IcmpHeader*)icmp_data)->i_cksum = checksum((USHORT*)icmp_data, datasize);//计算校验和 bwrote = sendto(m_hSocket, icmp_data, datasize, 0, (struct sockaddr*)&m_addrDest, sizeof(m_addrDest));//开始发送ICMP请求 if (bwrote == SOCKET_ERROR)//如果发送不成功 { if (WSAGetLastError() == WSAETIMEDOUT) //如果是由于超时不成功 { printf(“Requrest timed out ! \r\n“); continue; } printf(“目标不可达!\t错误代码 %d\n“, WSAGetLastError());//其他发送不成功原因 continue; } if (bwrote < datasize) { printf(“Wrote %d bytes \r\n“, bwrote); } int fromlen = sizeof(m_addrFrom);//开始接收ICMP应答 bread=recvfrom(m_hSocket,recvbuf,MAX_PACKET,0,(struct sockaddr*)&m_addrFrom, &fromlen); if (bread == SOCKET_ERROR)//如果接收不成功 { if (WSAGetLastError() == WSAETIMEDOUT) //如果是由于超时不成功 { printf(“Requrest timed out !\r\n“); continue; } printf(“接收数据函数调用错误!\t错误代码 %d\n“, WSAGetLastError());//其他接收不成功原因 exit(0); } DecodeICMPHeader(recvbuf, bread, &m_addrFrom);//解读接收到的ICMP数据报 } Cleanup(); } 八、心得体会 此次我们的课程设计的课题是ping程序的实现,之前我们的实验课上是在局域网中通过路由器交换机等设备由

推荐访问:
上一篇:2021年九年级第一学期期末班主任工作总结
下一篇:工程项目策划书参考范文(二)

Copyright @ 2013 - 2018 优秀啊教育网 All Rights Reserved

优秀啊教育网 版权所有