计算机网络课程设计报告
设计名称 Ping程序设计实现
专业班级
组姓名
指导教师
成 绩
设计目求
1实验目
利ICMP数包C语言实现Ping命令程序实现基Ping操作发送ICMP回显请求报文测试—机机间连通情况通程序训练学生熟悉ICMP报文结构学生ICMP更深理解掌握Ping程序设计方法掌握网络编程方法技巧编写出功更强程序
2 实验求:输出参考系统带ping程序命令行运行ping ip
二 设计说明
1. 设计思路
Ping程序面户应程序该程序ICMP封装机制通IP协议工作实现直接IPICMP包进行操作实验中RAW模式socket编程首先定义IP数报首部IP数报基础定义ICMP数报首部初始化全局变量接着定义填充ICMP数报字段函数FillICMPData()校验函数checksum()解读ICMP报首部函数DecodeICMPHeader()释放资源函Cleanup()
函数通调函数实现Ping命令功
2 设计方案
IP头ICMP头设置分参RFC791RFC792标准包含必信息程序设置main()函数函数库函数实现套接字编程数包发送接收中数包发送调sendto()数包接收调recvfrom( )发送数包时会遇阻塞者目标机通造成超时需发送数包调函数判断否超时处调库函数setsockopt()实现超时判断次校验函数采移位方法进行计算
3 系统运行环境:VC++ 60 Window XP操作系统台
4 设计中难点重点
首先遇问题套接字文件问题套接字需文件头文件Winsocket2h库文件WS2_32LIB动态库W32_32DLL创建套接字时候参数创建套接字前必须首先WSAStartup函数完套接字释放存资源关闭套接字问题前未接触写程序时候需查阅量资料弄懂问题次套接字问题解决遇难题较重问题实现ICMP报文发送接受样判断发送接收超时者找目机程序调试时候总出现样样错误头文件错误动态库法导入编辑器环境匹配等
5 输入输出条件
程序运行弹出命令行界面输入IP址例:596917218回车键短时间输出相关信息方没收会Requrest timed out 输出
程序运行次输出四行结果(前提输入址效)欲次Ping址必须重新运行程序输入时输入目标机名然ping结果***’机
三 系统详细设计
程序通main()函数调定义函数身功例:开socket动态库设置接收发送超时值域名址解析分配存创建初始化ICMP报文发送ICMP请求报文接收ICMP 应答报文解读应答报文输出Ping结果释放占资源流程图示
开始
定义初始化全局变量
判断WSAStartup函数否调成功
输出调失败
否
创建套接字设置socket接收超时发送超时选项
输入PINGIP址
解析输入容设置PING参数
创建填充ICMP数报文
判断否已发送四次
Break
发送接收解析数包
输出PIING结果
结束
清残余
否
校验函数释放资源函数:
Checksum开始
定义初始化cksum
(size > 1)
确定cksumsize
if (size)
计算校验cksum获结果
cksum + *(UCHAR*)buffer
否
结束
Cleanup开始
if (m_hSocket INVALID_SOCKET)
关闭套接字
释放占资源
清ICMP包数接受缓区
F
WSACleanup()
结束
解读ICMP报首部函数流程图:
DecodeICMPHeader开始
定义相关变量初始化
tick GetTickCount()
回应报文
输出
tick0[icmpcount]tick icmphdr>timestamp
判断时间否1ms
printf(Reply from s dytesd time<1ms icmp_seq d\ninet_ntoa(from>sin_addr) bytes icmphdr>i_seq )
printf(Reply from s dytesd timed icmp_seq d\ninet_ntoa(from>sin_addr) bytestick0[icmpcount] icmphdr>i_seq)
icmpcount++
结束
结束
否
四程序源代码注释
#pragma comment(libws2_32lib)
#include
#include
#include
#include
#include
typedef struct iphdr
{
unsigned int h_len4 头长度
unsigned int version4 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 datasizeDEF_PACKET_SIZE
char *icmp_dataNULL
char *recvbufNULL
SOCKET m_hSocket INVALID_SOCKET
char *lpdestNULL
填充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 cksum0
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\ninet_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 bytesd time<1ms icmp_seq d\ninet_ntoa(from>sin_addr) bytes icmphdr>i_seq )
else
printf(Reply from s bytesd timedms icmp_seq d\ninet_ntoa(from>sin_addr) bytestick0[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()
}
函数
void main()
{
WSADATA wsaData
char a[100]
printf(ping )
scanf(sa)
lpdesta
SOCKADDR_IN m_addrDest结构体
SOCKADDR_IN m_addrFrom
int timeout1000
USHORT seq_no0
if (WSAStartup(MAKEWORD(2 2) &wsaData) 0)
{
printf(Sorry you cannot load socket dll)
}
m_hSocket WSASocket (AF_INET SOCK_RAW IPPROTO_ICMP NULL 0WSA_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_addrDestsin_family AF_INET设置址族里表示IP址族
if ((m_addrDestsin_addrs_addr inet_addr(lpdest)) INADDR_NONE)址转化
{
struct hostent *hp NULL
if ((hp gethostbyname(lpdest)) NULL) 名字解析根机名获取IP址
{
memcpy(&(m_addrDestsin_addr) hp>h_addr hp>h_length)获取IP值赋目址中相应字段
m_addrDestsin_family hp>h_addrtype 获取址族值赋目址中相应字段
}
else
{
printf(找名 s 机\t错误代码 d\nlpdest WSAGetLastError())获取成功
exit(0)
}
}
printf(Pinging s with 64 bytes of data \n\n inet_ntoa(m_addrDestsin_addr))
datasize + sizeof(IcmpHeader) 数报文需包含ICMP报头
根默认堆句柄堆中分配MAX_PACKET存块新分配存容初始化0
icmp_data(char*)HeapAlloc(GetProcessHeap()HEAP_ZERO_MEMORYMAX_PACKET)
recvbuf (char*) HeapAlloc(GetProcessHeap() HEAP_ZERO_MEMORYMAX_PACKET)
if (icmp_data) 果分配存成功
{
printf(堆分配错误)
}
memset(icmp_data0MAX_PACKET)创建ICMP报文
FillICMPData(icmp_datadatasize)
开始发送接受ICMP包
int nCount0
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应答
breadrecvfrom(m_hSocketrecvbufMAX_PACKET0(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()
}
五 相关注释
1GetCurrentProcessId()
函数原型:DWORD WINAPI GetCurrentProcessId(VOID)
说明 获取前进程标示符(PID)
返回值:返回标示符(PID)
库文件:kernel32dll
2 GetTickcount()
函数原型DWORD GetTickCount(void)
说明:常常判断某方法执行时间
返回值:返回操作系统启动前毫秒数返回值32位双字类型DWORD存储存储值21 ms约4971天系统运行时间超4971天时数会0MSDN中明确提Retrieves the number of milliseconds that have elapsed since the system was started up to 497 days果编写服务器端程序处定万分注意避免引起意外状况
3inet_ntoa()
函数功:网络址转换成点隔字符串格式
需库:winsockh :头文件 Winsock2h
函数原型:char FAR* PASCAL FAR inet_ntoa( struct in_addr in) in:表示Internet机址结构
注释:函数in参数表示Internet址结构转换成 间隔诸abcd字符串形式请注意inet_ntoa()返回字符串存放WINDOWS套接口实现分配存中应程序应假设该存分配线程WINDOWS套接口调前数保证效
返回值:错误发生inet_ntoa()返回字符指针否话返回NULL中数应WINDOWS套接口调前复制出
4HeapFree()
功:堆中释放HeapAllocHeapReAlloc函数定位存块
语法BOOL WINAPI HeapFree(
__in HANDLE hHeap
__in DWORD dwFlags
__in LPVOID lpMem
)
果函数成功返回值非零果失败返回零值应程序通调GetLastError函数获取更错误信息
存块释放时候次进行引信息消失果想信息请释放包含信息存诸够返回存信息(存)等函数释放存效存指针进行两次释放会导致堆崩溃
5 getprocessheap() 获取调程堆句柄
6 WSACleanup()
功中止Winsock 2 DLL (Ws2_32dll)
头文件Winsock2h
函数原型int PASCAL FAR WSACleanup ( void )
返回值操作成功返回值0否返回值SOCKET_ERROR通调WSAGetLastError获取错误代码线程环境WSACleanup()中止Windows Sockets线程操作
7 closesocket()
简述:关闭套接口
头文件:#include
函数原型:int PASCAL FAR closesocket( SOCKET s)
s:套接口描述字
返回值:错误发生closesocket()返回0否话返回SOCKET_ERROR错误应程序通WSAGetLastError()获取相应错误代码
8 WSAStartup()
简述:创建指定传送服务提供者捆绑套接口选创建加入套接口组函数socket()扩展版功创建原始套接字时需包含 winsock2h 头文件链接wsock32lib库
定义:SOCKET WSAAPI WSASocket ( int af int type int protocol LPPROTOCOL_INFO lpProtocolInfo Group g int iFlags)
af:址族描述目前仅支持AF_INET格式ARPA Internet址格式
type:新套接口类型描述
protocol:套接口特定协议果调者愿指定协议定0 lpProtocolInfo:指PROTOCOL_INFO结构指针该结构定义创建套接口特性果参数非零前三参数(af type protocol)忽略
g:套接口组描述字
iFlags:套接口属性描述
返回值:错误发生WSASocket()返回新套接口描述字否话返回 INVALID_SOCKET应程序定调WSAGetLastError()获取相应错误代码
9 setsockopt()
功:设置套接口选项
头文件:#include
函数原型:int PASCAL FAR setsockopt( SOCKET s int level int optname const char FAR *optval int optlen)
s:标识套接口描述字
level:选项定义层次目前仅支持SOL_SOCKETIPPROTO_TCP层次
optname:需设置选项
optval:指针指存放选项值缓区
optlen:optval缓区长度
10memset()
函数原型:void *memset(void *sint csize_t n)
总作:已开辟存空间 s 首 n 字节值设值 c
memset()深刻涵:段存空间全部设置某字符般定义字符串进行初始化memset(a '\0' sizeof(a))
11 gethostbyname()
说明:返回应定机名包含机名字址信息hostent结构指针
返回应定机名机信息
头文件:#include
函数原型:struct hostent FAR *PASCAL FAR gethostbyname(const char FAR * name)
name:指机名指针
返回值:果没错误发生gethostbyname()返回述指hostent结构指针否返回空指针应程序通WSAGetLastError()特定错误代码
12memcpy()
函数原型:void *memcpy(void *dest const void *src size_t n)
说明:src指址起始址连续n字节数复制dest指址起始址空间
头文件:#include
返回值:函数返回指dest指针strcpy相memcpy遇'\0'结束定会拷贝完n字节
13 WSAGetLastError()
简述 获次失败操作错误状态
头文件:#include
函数原型:int PASCAL FAR WSAGetLastError ( void )
注释 函数返回次发生网络错误特定Windows Sockets API函数指出错误已发生函数应调获应错误代码
返回值 返回值指出线程进行次Windows Sockets API函数调时错误代码
14sendto()
说明:指定目发送数
头文件:#include
函数原型:int PASCAL FAR sendto( SOCKET s const char FAR* buf int len int flags const struct sockaddr FAR* to int tolen)
s服务器端监听套接字
buf欲发送数缓区指针
len发送数缓区长度
flags数发送标记
to:(选)指针指目套接口址
tolen:to指址长度sizeof(tolen)
返回值:发送数字符数
注释:sendto()适已连接数报流式套接口发送数数报类套接口必需注意发送数长度应超通讯子网IP包长度IP包长度WSAStartup()调返回WSADataiMaxUdpDg元素中果数太长法动通层协议返回WSAEMSGSIZE错误数会发送
请注意成功完成sendto()调意味着数传送达sendto()函数SOCK_DGRAM类型套接口to参数指定端套接口发送数报SOCK_STREAM类型套接口totolen参数忽略种情况sendto()等价send()
15 recvfrom()
函数原型ssize_t recvfrom(int sockfdvoid *bufint lenunsigned int flags struct sockaddr *fromsocket_t *fromlen)
ssize_t 相 intsocket_t 相int 里名字提高代码说明性 函数说明 recvfrom()接收远程机指定socket传数数传参数buf指存空间参数len接收数长度参数flags般设0数值定义参考recv()参数from指定欲传送网络址结构sockaddr请参考bind()函数参数fromlensockaddr结构长度
返回值 成功返回接收字符数失败返回1
16 结构体sockaddr_in
Step 1 初始化该数结构
struct sockaddr_in my_addr
Step 2 填充信息
my_addrsin_family AF_INET
my_addrsin_port htons (6666)
my_addrsin_addrs_addr htonl (INADDR_ANY)
my_addrsin_addrs_addrip址
六 实验数结果分析
Ping意IP址:
连接网络情况Ping域名:
Ping IP址超时:
Ping返回结果分析:
1Request timed out(请求超时)
(1) 方已关机者网络根没址:图中Ping 596917253
(2)方网段通路法找方时方确实存然存返回超时信息
(3)方确实存设置ICMP数包滤(防火墙设置)
(4)错误设置IP址
正常情况台机应该网卡IP址网卡IP址(址定处IP子网)果台电脑拨号网络适配器(相块软网卡)TCPIP设置中设置网卡IP址处子网IP址样IP层协议台机两接口处网段台机Ping机器时会存样问题:
A机知道数包发网络接口两网络接口连接网段
B机知道址作数包源址台机Ping机器IP层协议会法处理超时Ping 会出超时应答错误信息提示机Ping台机时请求包特定网卡ICMP须简单目源址互换更改标志ICMP应答包利发出机成功Ping通台机器
2Destination host Unreachable(目标达)
(1)方网段未设置默认路
(2)网线出障
里说明destination host unreachable time out区果路器路表中具达目标路目标原达时候会出现time out果路表中连达目标路没会出现destination host unreachable
3Unknown host——知名机
种出错信息意思该远程机名字域名服务器(DNS)转换成IP址障原域名服务器障者名字正确者网络理员系统远程机间通信线路障
7Ping 127001:127001循环址
六总结
通设计程序真正解ICMP结构编写程中基常见函数会应发现知识匮乏学程中努力阅读复杂程序解基函数算法精良编程思想更动手写定难度程序应该害怕写程序出错应该胆写出想法出现错误解决错误找出知识漏洞模糊点通阅读错误程序试着帮查找错误样证书技头脑中规发现初学者番错误少走弯路
通整程序编写文档程感觉力远远没达老师求层面恐怕继续努力啊
文档香网(httpswwwxiangdangnet)户传
《香当网》用户分享的内容,不代表《香当网》观点或立场,请自行判断内容的真实性和可靠性!
该内容是文档的文本内容,更好的格式请下载文档