2024年4月17日发(作者:)
计算机科学与技术学院课程设计成绩单
课程名称: 计算机网络 指导教师: XX
XX 2009xxxxx
姓名 性别 男 学号 班级 软件xxxx
综合成绩
程序运行情况
(占总成绩20%)
成绩等级
□
能正确运行 □基本能正确运行 □能运行但结果不完善
(20分) (15分) (10分)
程序功能的完善
程度 □完善 □基本完善 □不完善
(占总成绩10%) (10分) (8分) (5分)
程序结构的合理
性 □合理 □基本合理 □不太合理
(占总成绩10%) (10分) (8分) (5分)
对问题的答辩情□概念正确有创新 □能正确回答所有问题 □基本能正确回答
况 (40分) (35分) (30分)
(占总成绩40%) □部分问题回答概念不清晰
(20分)
学生的工作态度□工作态度认真能独立完成任务 □工作态度认真但独立性较差
与独立工作能力 (10分) (8分)
(占总成绩10%) □工作态度基本认真但缺乏独立性
(5分)
设计报告的规范
性 □符合规范 □基本符合规范 □规范性较差
(占总成绩10%) (10分) (8分) (5分)
优秀:90分~100分 良好:80分~89分 中等:70~79分 及格:60~69分 不及格0分~59分
武汉科技大学计算机科学与技术学院制表
课 程 设 计 报 告
计算机科学与技术学院
课程名称: 计算机网络
专 业: xxxxxxxx
班 级: xxxx级 xx 班
学 号: 20xxxxxxxxxx
姓 名: xxxxxxx
指导老师: xxxxxxxx
概要设计
1.1题目的内容与要求
题目内容:PING程序是我们使用的比较多的用于测试网络连通性的程序。
PING程序基于ICMP,使用ICMP的回送请求和回送应答来工作。其中,ICMP
是基于IP的一个协议,ICMP包通过IP的封装之后传递。PING程序是面向用户
的应用程序,该程序使用ICMP的封装机制,通过IP协议来工作。为了实现直接
对IP和ICMP包进行操作,实验中使用SOCKET编程机制。
设计要求:
1、SOCKET编程
熟悉SOCKET的编程,包括基本的系统调用如SOCKET、BIND等。
2、具体内容
1) 定义数据结构
需要定义好IP数据报、ICMP包等相关的数据结构
2) 程序实现
在WINDOWS环境下实现PING程序
3) 程序要求
在命令提示符下输入:
PING ΧΧΧ.ΧΧΧ.ΧΧΧ.ΧΧΧ
其中ΧΧΧ为目的主机的IP地址,不要求支持域名,对是否带有开关变量也
不做要求。不带开关变量时,要求返回4次响应。
返回信息的格式:
REPLY FROM ΧΧΧ.ΧΧΧ.ΧΧΧ.ΧΧΧ
或
REQUEST TimeOut (无法PING通的情况)
4)编程语言不限。
需求分析和详细设计
2.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.1 主模块流程图
2.2功能控制模块
功能控制模块主要是为其他模块提供可调用的函数,该模块主要包括参数获
取功能、计算ICMP数据报文检验和、清除SOCKET,ICMP包数据以及接受缓冲
区、占用资源释放功能和显示用尸帮助功能。该模块一共包含三个函数来实现。,
流程如图2.2所示。
Checksum开始 Cleanup开始
定义初始化cksum
if (m_hSocket !=
INVALID_SOCKE
(size > 1)
是
确定cksum及size大小
释放
占用资源
关闭
套接
字 F
清除ICMP包数据
以及接受缓冲区
if (size)
cksum +=
*(UCHAR*)
buffer;
WSACleanup();
否
计算校验cksum,获得结果
结束
结束
图2.2 功能控制模块
注释:
(1) illICMPData是由一系列的初始化的语句在流程图中不再画出;
(2) Cleanup() 函数中的WSACleanup(),HeapFree(),closesocket()都是
一些库函数。
(3) checksum()校验和函数是冗余校验的一种形式。 它是通过错误检测方
法,对经过空间(如通信)或者时间(如计算机存储)传送的数据的完整性进行检
查的一种简单方法。
2.3数据报解析模块
数据报解析模块提供了解读IP选项和解读IcMP报文的功能。从本机收到目
的主机返回的1cMP回显应答报文,就开始逐个地解读IcMP报文,如果需要记
录路由的情况下,IcMP解析函数将调用IP选项解读函数来实现IP路由的输出(但
本程序没有此功能。该模块主要由DecodeICMPHeader一个函数来实现,而中间
也会调用其它模块的相应函数。其流程图如图2.3:
注释:
(1) 判断是否为我们所要的数据报回应之前,还有一些判断回应多少内容的
语句未呈现出;
(2) 函数GetTickCount()是用来记录此时我机所处的现在时间(毫秒级);
DecodeICMPHeader
定义相关变量以及初始化;
tick = GetTickCount();
为我们所要的回应报
文;
否
是
输出不是我们所要;
tick0[icmpcount]=tick
icmphdr->timestamp;
-
结束
判断时间是否小于1ms
是
是
printf("Reply from %s: dytes=%d
time<1ms icmp_seq
= %dn",inet_ntoa(from->sin_addr),
printf("Reply from %s: dytes=%d
time=%d icmp_seq
= %dn",inet_ntoa(from->sin_addr),
icmpcount++;
结束
图2.3 数据报解析模块
运行界面
运行操作及结果:
在VC中运行程序后会出现如图4.1所示,提示你输入IP或网址;
图4.1开始输入
下面我们先以输入室友的IP地址为例进行试验,结果如图4.2;
图4.2测试本地IP
接着我们任意输入网络地址,在联网的情况下,结果如图4.3;
图4.3测试网络IP
但是当断开网络连接时时,就会出现下图结果:
图4.网络IP不通
附 录(程序清单)
#pragma comment(lib,"ws2_")
#include
#include
#include
#include
#include
typedef struct _iphdr
{
unsigned int h_len:4; // 头长度
unsigned int version:4; // IP版本
unsigned char tos; // 服务类型
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
#define ICMP_ECHOREPLY 0
#define ICMP_MIN 8 // 最小8字节的包头
typedef struct _icmphdr
{
BYTE i_type;
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;
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; // 要求有IP回应
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);
}
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 rn",inet_ntoa(from->sin_addr));
}
icmphdr = (IcmpHeader*)(buf + iphdrlen);
if (icmphdr->i_type != ICMP_ECHOREPLY)
{
printf("nonecho type %d recvd rn", icmphdr->i_type);
return;
}
// 确定是我们发的数据包回应
if (icmphdr->i_id != (USHORT)GetCurrentProcessId())
{
printf("其他程序的回应报文! t错误代码 %dn",
WSAGetLastError());
return ;
}
DWORD tick0[4];
tick0[icmpcount]=tick - icmphdr->timestamp;
if(tick0[icmpcount]<1)
icmp_seq printf("Reply from %s: bytes=%d time<1ms
= %dn",inet_ntoa(from->sin_addr), bytes, icmphdr->i_seq );
else printf("Reply from %s: bytes=%d time=%dms icmp_seq
= %dn",inet_ntoa(from->sin_addr), bytes,tick0[icmpcount], icmphdr->i_seq);
icmpcount++;
return;
}
void Cleanup()
{
if (m_hSocket != INVALID_SOCKET)
closesocket(m_hSocket);
HeapFree(GetProcessHeap(), 0, recvbuf);
HeapFree(GetProcessHeap(), 0, icmp_data);
WSACleanup();
return ;
}
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);
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));
// 如果必要的话需要对输入IP进行解析
m__family = AF_INET;
if ((m__addr.s_addr = inet_addr(lpdest)) == INADDR_NONE)
{
struct hostent *hp = NULL;
if ((hp = gethostbyname(lpdest)) != NULL)
{
memcpy(&(m__addr), hp->h_addr, hp->h_length);
m__family = hp->h_addrtype;
}
else
{
printf("不能找到名为 %s 的主机t错误代码 %dn",lpdest,
WSAGetLastError());
exit(0);
}
}
printf("Pinging %s with 64 bytes of data: nn", inet_ntoa(m__addr));
// 创建ICMP包
datasize += sizeof(IcmpHeader);
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);
FillICMPData(icmp_data,datasize);
// 开始发送或接受ICMP包
int nCount=0;
while(1)
{
int bwrote;
if (nCount++ == 4)
break;
((IcmpHeader*)icmp_data)->i_cksum = 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));
if (bwrote == SOCKET_ERROR)
{
if (WSAGetLastError() == WSAETIMEDOUT)
{
printf("Requrest timed out ! rn");
continue;
}
printf("目标不可达!t错误代码 %dn", WSAGetLastError());continue;
}
if (bwrote < datasize)
{
printf("Wrote %d bytes rn", bwrote);
}
int fromlen = sizeof(m_addrFrom);
bread = recvfrom(m_hSocket, recvbuf, MAX_PACKET, 0,
(struct sockaddr*)&m_addrFrom, &fromlen);
if (bread == SOCKET_ERROR)
{
if (WSAGetLastError() == WSAETIMEDOUT)
{printf("Requrest timed out !rn");
continue;
}
printf("接收数据函数调用错误!t错误代码
WSAGetLastError());
exit(0);
}
DecodeICMPHeader(recvbuf, bread, &m_addrFrom);
}
Cleanup();
}
%dn",
课程设计总结:
通过设计程序,真正了解了ICMP的结构。编写过程中,一些基本的常见的
函数不会应用,这使我发现自己知识的匮乏,在以后的学习过程中得要好好的努
力,多阅读一些复杂的程序,了解一个基本的函数,算法和精良的编程思想,更
要多动手写一些有一定难度的程序,我们不应该害怕写程序出错,应该大胆地写
出自己的想法,出现错误去解决错误就能找出自己知识的漏洞和模糊点。我们还
可以通过阅读别人错误的程序,试着帮别人查找错误,这样证书技能头脑中的规
则还能发现一些初学者一番的错误,使自己少走弯路。
通过整个程序和编写文档过程,感觉自己的能力还远远没达到老师的要求的
层面上,恐怕还要继续努力啊。
指导教师评语:
指导教师(签字): 年 月 日
课程设计成绩


发布评论