《网络通信协议分析》课程设计开发实例Word文档格式.docx
《《网络通信协议分析》课程设计开发实例Word文档格式.docx》由会员分享,可在线阅读,更多相关《《网络通信协议分析》课程设计开发实例Word文档格式.docx(25页珍藏版)》请在冰豆网上搜索。
CRC校验码的检错能力很强,不仅能检查出离散错误,还能检查出突发错误。
利用CRC进行检错的过程可简单描述如下:
在发送端根据要传送的k位二进制码序列,以一定的规则产生一个校验用的r位监督码(CRC码),附在原始信息的后边,构成一个新的二进制码序列(共k+r位),然后发送出去。
在接收端,根据信息码和CRC码之间所遵循的规则进行检验,以确定传送中是否出错。
这个规则在差错控制理论中称为“生成多项式”。
CRC的基本实现
循环冗余校验码的特点:
(1)CRC校验码可检测出所有单个错误。
(2)CRC校验码可检测出所有奇数位错误。
(3)CRC校验码可检测出所有双位的错误(4)CRC校验码可检测出所有小于、等于校验位长度的突发错误。
(5)CRC校验码可以
的概率检测出长度为(K+1)位的突发错误
实验分析:
•填充帧头部字段
要完成一次帧封装的过程,首先要完成的就是帧头部的装入,这一过程只要将签到吗、定界符、目的地址、源地址、长度字段的相应数值按顺序写入就可以了。
其中,长度字段的值即为要发送的数据的实际长度。
•填充数据字段
在填充数据字段的过程中要注意的主要问题是数据字段的长度。
802.3标准中规定了帧数据字段的最小长度为46B,最大长度为1500B。
如果数据不足46B,则需要通过填充0来补足;
若数据长度超过1500B,则的大奖超过部分封装入下一个帧进行发送。
•CRC校验
帧封装的最后一步就是对数据进行校验,并将校验结果记入帧校验字段。
程序流程图:
CRC计算流程图:
序源代码:
#include<
iostream.h>
fstream.h>
stdlib.h>
voidmain(intargc,char*argv[])
{
//如果输入命令行不正确,则输出提示后退出。
if(argc!
=3)
{
cout<
<
endl<
"
请按以下格式输入:
framerinputfileoutputfile"
endl;
exit(0);
}
//打开指定的输出文件,以二进制方式打开并可读可写,如文件存在,则清除其内容。
fstreamfile(argv[2],ios:
:
out|ios:
in|ios:
binary|ios:
trunc,0);
for(inti=0;
i<
7;
i++)
file.put((char)0xaa);
file.put((char)0xab);
//写入B的前导码和B的帧前定界符。
chardes_add[]={char(0x00),char(0x00),char(0xE4),char(0x86),char(0x3A),char(0xDC)};
file.write(des_add,6);
//写入B的目的地址。
charsor_add[]={char(0x00),char(0x00),char(0x80),char(0x1A),char(0xE6),char(0x65)};
file.write(sor_add,6);
//写入B的源地址。
//创建输入文件流并打开指定的输入文件,以二进制方式打开并可读。
ifstreaminfile(argv[1],ios:
binary,0);
intlength=0;
infile.seekg(0,ios:
end);
//将读指针移到文件末尾。
length=infile.tellg();
//计算指针偏移量,即为输入文件的长度。
unsignedchar*data=newunsignedchar[length];
//创建字符指针并根据文件长度初始化。
beg);
//将读指针移到文件开始。
infile.read(data,length);
//将文件数据读入到字符指针data中。
file.put(char(length>
>
8));
file.put(char(length&
0xff));
//将文件长度值按照逆序写入到输出文件的长度字段中。
file.write(data,length);
//将data内容写入到输出文件中。
//如果输入文件长度不足B,则用补足B。
if(length<
46)
for(intj=length;
j<
46;
j++)
file.put(char(0x00));
//将数据字段后添加个
file.seekg(8,ios:
//将读指针指向目的地址字段,从此处开始CRC计算
unsignedcharch;
//ch用来保存读入的字符。
unsignedcharcrc=char(0x00);
//余数初始值为。
while
(1)//进行CRC计算
file.get(ch);
if(ch==0xff)//判断是否到了文件结尾,如果是,则退出循环。
break;
for(i=0;
8;
i++)//对入读入的字符的位分别处理。
if(0x80==(crc&
(0x80)))//当前余数最高位为,需要进行除法运算。
crc=(crc<
1)&
(0xff);
//crc左移位,最低位补。
crc=crc|((ch&
0x80)>
7);
//将输入数据相应的值递补到余数末位。
crc=crc^(0x07);
//进行除法运算,即与除数的低位相异或。
else//当前余数的最高位为,不需要进行除法运算。
//将输入数据相应位的值递补到余数末位。
ch=ch<
1;
//读到的字符左移位,使数据下一位作为输入位。
file.clear();
file.seekp(-1,ios:
//将写指针移到输出文件的最后。
file.put(crc);
//写入crc码。
file.close();
infile.close();
//关闭输入文件和输出文件。
数据帧文件"
argv[2]<
封装完成"
}
运行结果:
运行结果如下所示:
执行framer.exe文件的结果如下所示:
实验小结:
实现帧的封装,主要是将帧的七个部分---前导码、帧前定界符、目的地址、源地址、长度字段、数据字段和校验字段,一个一个按顺序封装的,最后使得一个帧的封装得以完成。
同时,在编写程序的过程中,用到了很多的函数,这些函数的运用使得程序简便而且正确的运行出来。
实验二解析IP数据包
•设计一个解析IP数据包的程序,并根据这个程序,说明IP数据包的结构及IP协议的相关问题,从而对IP层的工作原理有更好的理解和认识。
实验要求:
本实验的目标是捕获网络中的IP数据包,解析数据包的内容,见个结果显示在标准输出上,并同时写入日志文件。
程序的具体要求如下:
•以命令行形式运行:
ipparselogfile,其中ipparse是程序名,而logfile则代表记录结果的日志文件。
•在标准输出、和日志文件中写入捕获的IP包的版本、头长度、服务类型、数据包总长度、数据包标识、分段标志、分段偏移值、生存时间、上层协议类型、头校验和、源IP地址和目的IP地址等内容。
•当程序接收到键盘输入Ctrl+C时退出。
设计相关知识:
IP数据报的格式说明IP协议都具有什么功能。
其首部,版本目前广泛使用的版本号为4;
首部长度站4bit;
服务类型占8bit,其中服务类型TOS子域占4位,优先级子域占3位,另一位为保留位;
总长度字段为2B,IP数据包的最大长度是65535B;
标识占16bit,它是一个计数器,用来产生数据报的标识;
标志占3bit,其中最低为为MF,MF=1时为后面“还有分片”,MF=0表示这是数据报片中的最后一个,DF=0时,表示允许分片;
片偏移以8个字节为偏移单位;
生存时间字段记为TTL,单位为秒;
协议段占8bit,用于指出次数据是使用何种协议,典型的协议号有6:
TCP,17:
UDP,1:
ICMP。
本程序使用套接字socket编程,将网卡设为能够接受流经网卡的所有类型的数据包。
首先,初始化套接字,然后监听数据包,解析数据包。
SOCKETsock=socket(AF_INET,SOCK_RAW,IPPROTO_IP)用来创建套接字,其参数为通信发生的区字段和套接字的类型。
WSAIoctl(sock,IO_RCVALL,&
dwBufferInLen,sizeof(dwBufferInLen)函数用来把网卡设置为混杂模式。
recv(sock,buffer,65535,0)函数用来接收经过的IP包,其参数分别是套接字描述符,缓冲区的地址,缓冲区的大小。
typedefstructIP_HEAD
}}ip_head;
用来定义IP头部数据。
setsockopt(sock,IPPROTO_IP,IP_HDRINCL,(char*)函数用来获取本机IP地址
htons()函数将无符号短整型转换为网络字节顺序的数据
本程序在windows环境下利用C++语言编写。
实验设计分析:
为了获取网络中的IP数据包,必须对网卡进行编程,我们使用套接字进行编程。
•使用套接字
•
接收数据包
•定义IP头部的数据结构
•IP包的解析
具体程序代码:
iostream>
winsock2.h>
ws2tcpip.h>
fstream>
windows.h>
#pragmacomment(lib,"
ws2_32"
)//指定连接到网络应用和internet
#defineIO_RCVALL_WSAIOW(IOC_VENDOR,1)
typedefstructIP_HEAD
{
union//定义联合
unsignedcharVersion;
unsignedcharHeadLen;
};
unsignedcharServiceType;
unsignedshortTotalLen;
unsignedshortIdentifier;
union
unsignedshortFlags;
unsignedshortFragOffset;
unsignedcharTimeToLive;
unsignedcharProtocol;
unsignedshortHeadChecksum;
unsignedintSourceAddr;
unsignedintDestinAddr;
unsignedcharOptions;
}ip_head;
//定义IP头部的数据结构
voidmain(intargc,char*argv[])
usingnamespacestd;
ofstreamoutfile("
C:
\\logfile.txt"
ios:
out);
=2)
请以下格式输入命令行:
PackParsepacket_sum"
return;
}
WSADATAWSAData;
if(WSAStartup(MAKEWORD(2,2),&
WSAData)!
=0)
WSASTartup初始化失败"
SOCKETsock=socket(AF_INET,SOCK_RAW,IPPROTO_IP);
//三个参分别为通信发生的区字段,套接字的类型,与IP协议
if(sock==INVALID_SOCKET)
创建Socket失败!
closesocket(sock);
WSACleanup();
BOOLflag=TRUE;
if(setsockopt(sock,IPPROTO_IP,IP_HDRINCL,(char*)&
flag,sizeof(flag))==SOCKET_ERROR)
setsockopt操作失败:
WSAGetLastError()<
charhostName[128];
//获取主机名
if(gethostname(hostName,100)==SOCKET_ERROR)
gethostname操作失败:
hostent*pHostIP;
//获取本地IP
if((pHostIP=gethostbyname(hostName))==NULL)
gethostbyname操作失败:
sockaddr_inhost_addr;
//
host_addr.sin_family=AF_INET;
host_addr.sin_port=htons(6000);
host_addr.sin_addr=*(in_addr*)pHostIP->
h_addr_list[0];
if(bind(sock,(PSOCKADDR)&
host_addr,sizeof(host_addr))==SOCKET_ERROR)
bind操作失败:
//绑定网卡
DWORDdwBufferLen[10];
DWORDdwBufferInLen=1;
DWORDdwBytesReturned=0;
if(WSAIoctl(sock,IO_RCVALL,&
dwBufferInLen,sizeof(dwBufferInLen),&
dwBufferLen,sizeof(dwBufferLen),&
dwBytesReturned,NULL,NULL)==SOCKET_ERROR)
WSAIoctl操作失败:
//将网卡设为混杂模式,以接受所有数据
开始解析IP包:
charbuffer[65535];
//设置缓冲区
intpacksum=atoi(argv[1]);
//字符串转换为整形
for(inti=0;
packsum;
i++)
if(recv(sock,buffer,65535,0)>
0)//四个参数分别是套接字描述符,缓冲区的地址,缓冲区大小,附加标志
ip_headip=*(ip_head*)buffer;
-----------------------"
版本:
(ip.Version>
4)<
//获取头部长度字段
头部长度:
((ip.HeadLen&
0x0f)*4)<
服务类型:
Priority"
(ip.ServiceType>
5)<
Service"
((ip.ServiceType>
0x0f)<
//优先级子域和TOS子域
总长度:
ip.TotalLen<
//获取总长度字段
标识符:
ip.Identifier<
//获取标识字段
标志位:
((ip.Flags>
15)&
0x01)<
DF="
14)&
Mf="
13)&
//获得标志字段
片偏移:
(ip.FragOffset&
0x1fff)<
//获取分段偏移字段
生存周期:
(int)ip.TimeToLive<
//获取生存时间字段
协议:
Protocol"
(int)ip.Protocol<
//获取协议字段
头部校验和:
ip.HeadChecksum<
//获取头校验和字段
原地址:
inet_ntoa(*(in_addr*)&
ip.SourceAddr)<
//获取源IP地址字段
目的IP地址:
ip.DestinAddr)<
//获取目的IP地址字段
outfile<
程序运行结果:
程序编译运行后:
以命令行形式运行程序ipparse:
同时在程序所在的文件夹中生成了名为logfile的txt文件,里面记录了上面显示的内容。
试验小结:
IP数据报的格式说明了IP协议都具有什么功能,因为完全不知道如何使用套接字socket()函,查阅了相关资料,了解了IP数据报的各种位与协议的概念和意义,通过解析IP数据包这个实验,基本掌握了用套接字编程来实现获取并解析IP数据包的方法。
实验三发送TCP数据包
•设计一个发送TCP数据包的程序,并根据本设计说明TCP数据包的结构以及TCP协议与IP协议的关系,使大家对TCP协议的工作原理有更深入的认识。
本程序的功能是填充一个TCP数据包,并发送给目的主机。
SendTCPsource_ipsource_portdest_ipdest_port
其中SendTCP为程序名;
source_ip为源IP地址;
source_port为源端口;
dest_ip为目的IP地址;
dest_port为目的端口。
•其他的TCP头部参数自行设定。
•数据字段为“Thisismyhomeworkofnetwork!
”.
•成功发送后在屏幕上输出“sendOK”。
课程设计分析:
•使用原始套接字
•定义IP头部、TCP头部和伪头部的数据结构
•填充数据包
•发送数据包
设计思想: