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的结构。编写过程中,一些基本的常见的

函数不会应用,这使我发现自己知识的匮乏,在以后的学习过程中得要好好的努

力,多阅读一些复杂的程序,了解一个基本的函数,算法和精良的编程思想,更

要多动手写一些有一定难度的程序,我们不应该害怕写程序出错,应该大胆地写

出自己的想法,出现错误去解决错误就能找出自己知识的漏洞和模糊点。我们还

可以通过阅读别人错误的程序,试着帮别人查找错误,这样证书技能头脑中的规

则还能发现一些初学者一番的错误,使自己少走弯路。

通过整个程序和编写文档过程,感觉自己的能力还远远没达到老师的要求的

层面上,恐怕还要继续努力啊。

指导教师评语:

指导教师(签字): 年 月 日

课程设计成绩