计算机网络课程设计 帧封装.docx
《计算机网络课程设计 帧封装.docx》由会员分享,可在线阅读,更多相关《计算机网络课程设计 帧封装.docx(20页珍藏版)》请在冰豆网上搜索。
计算机网络课程设计帧封装
学号:
课程设计
题目
帧封装
学院
计算机科学与技术学院
专业
软件工程专业
班级
软件0902班
姓名
指导教师
2012
年
6
月
20
日
课程设计任务书
学生姓名:
专业班级:
软件0902班
指导教师:
工作单位:
计算机学院
题目一:
帧封装
初始条件:
(1)学习相关知识
(2)C/C++/VC/VB/JAVA语言
(3)PC机一台
要求完成的主要任务:
(包括课程设计工作量及其技术要求,以及说明书撰写等具体要求)
编写程序,根据给出的原始数据,组装一个IEEE802.3格式的帧(题目默认的输入文件为二进制原始数据(文件名为input1和input2))。
1)要求程序为命令行程序。
比如,可执行文件名为framer.exe,则命令行形式如下:
framerinputfileoutputfile
其中,inputfile为原始数据文件,outputfile为输出结果。
使用操作系统、语言、编程环境不限,但在报告中必须注明。
2)输出:
对应input1和input2的结果分别为output1和output2。
时间安排:
第一、二天:
查阅资料,学习算法
第三、四天:
编程调试
第五天:
书写报告
指导教师签名:
年月日
系主任(或责任教师)签名:
年月日
帧封装
1.引言
以太网这个术语通常是指由DEC、Intel和Xerox公司在1982年联合公布的一个标准,它是当今TCP/IP采用的主要的局域网技术,它采用一种称作CSMA/CD的媒体接入方法。
在TCP/IP世界中,以太网IP数据报文的封装在RFC894中定义。
以太网采用广播机制,所有与网络连接的工作站都可以看到网络上传递的数据。
通过查看包含在帧中的目标地址,确定是否进行接收或放弃。
如果证明数据确实是发给自己的,工作站将会接收数据并传递给高层协议进行处理。
以太网采用CSMA/CD(CarrierSenseMultipleAccess/CollisionDetection)媒体访问机制,任何工作站都可以在任何时间访问网络。
在以太网中,所有的节点共享传输介质。
如何保证传输介质有序、高效地为许多节点提供传输服务,就是以太网的介质访问控制协议要解决的问题。
帧是在数据链路层数据进行传输与交换的基本单位。
构造帧对于理解网络协议的概念、协议执行过程以及网络问题处理的一般方法具有重要的意义。
本次课程设计的目的是应用数据链路层与介质访问控制层的知识,根据数据链路层的基本原理,通过构造一个具体的Ethernet帧,从而深入理解网络协议的基本概念与网络问题处理的一般方法。
2.以太网帧格式的发展
1980,DEC、Intel、Xerox制订了EthernetI的标准;
1982,DEC、Intel、Xerox又制订了EhternetII的标准;
1982,IEEE开始研究Ethernet的国际标准802.3;
1983,迫不及待的Novell基于IEEE的802.3的原始版开发了专用的Ethernet帧格式;
1985,IEEE推出IEEE802.3规范,后来为解决EthernetII与802.3帧格式的兼容问题,推出折衷的EthernetSNAP格式。
3.IEEE802.3帧结构
数据在网络上是以很小的称为帧(Frame)的单位传输的,帧由几部分组成,不同的部分执行不同的功能。
帧通过特定的称为网络驱动程序的软件进行成型,然后通过网卡发送到网线上,通过网线到达它们的目的机器,在目的机器的一端执行相反的过程。
接收端机器的以太网卡捕获到这些帧,并告诉操作系统帧已到达,然后对其进行存储。
“帧”数据大致由两部分组成:
帧头和帧数据。
帧头包括接收方主机物理地址的定位以及其它网络信息。
帧数据区含有一个数据体。
为确保计算机能够解释数据帧中的数据,这两台计算机使用一种公用的通讯协议。
互联网使用的通讯协议简称IP,即互联网协议。
IP数据体由两部分组成:
数据体头部和数据体的数据区。
数据体头部包括IP源地址和IP目标地址,以及其它信息。
数据体的数据区包括用户数据协议(UDP),传输控制协议(TCP),还有数据包的其他信息。
这些数据包都含有附加的进程信息以及实际数据。
8
6
6
2
46–1500
4
前导符
目的地址
源地址
长度
数据
FCS
图3.1IEEE802.3帧结构
常用的以太网MAC帧格式用两种标准,一种是DIXEthernetV2标准(即以太网V2标准),另一种是IEEE的802.3标准。
这里只介绍符合IEEE802.3标准的帧,其格式如图3.1所示。
它的组成比较简单,由6个字段组成。
接下来对这6个部分详细介绍一下。
(1)前导符:
由7字节的前同步码和1字节的帧起始定界符构成。
前同步码:
这个字段有7个字节(56位)交替出现的1和0,它的作用就是提醒接收系统有帧的到来,以及使到来的帧与计时器进行同步。
前同步码其实是在物理层添加上去的,并不是(正式的)帧的一部分。
前同步码的目标是允许物理层在接收到实际的帧起始符之前检测载波,并且与接收到的帧时序达到稳定同步。
帧起始定界符:
这个字段用1字节(10101011)作为帧开始的信号,表示一帧的开始。
最后两位是11,表示下面的字段是目的地址。
(2)目的地址(DA)48位,表示帧准备发往目的站的地址,共6个字节,可以是单址(代表单个站)、多址(代表一组站)或全地址(代表局域网上的所有站)。
当目的地址出现多址时,表示该帧被一组站同时接收,称为“组播”(Multicast)。
目的地址出现全地址时,表示该帧被局域网上所有站同时接收,称为“广播”(Broadcast),通常以DA的最高位来判断地址的类型,若第一字节最低位为“0”则表示单址,第一字节最低位为“1”则表示组播。
(3)源地址(SA)48位,表明该帧的数据是哪个网卡发的,即发送端的网卡地址。
(4)该字段是“长度/类型”。
当这个字段的值大于0X0600时(相当于十进制的1536),就表示“类型”。
这样的帧和以太网V2MAC帧完全一样。
只有当这个字段的值小于0X0600时才表示“长度”,即MAC帧的数据部分长度。
(5)数据字段的最小长度必须为46字节以保证帧长至少为64字节,这意味着传输一字节信息也必须使用46字节的数据字段:
如果填入该字段的信息少于46字节,该字段的其余部分也必须进行填充。
数据字段的默认最大长度为1500字节。
(6)帧检验序列(FCS)是32位冗余检验码(CRC),检验除前导、SFD和FCS以外的内容。
当发送站发出帧时,一边发送,一边逐位进行CRC检验。
最后形成一个32位CRC检验和填在帧尾FCS位置中一起在媒体上传输。
接收站接收后,从DA开始同样边接收边逐位进行CRC检验。
最后接收站形成的检验和若与帧的检验和相同,则表示媒体上传输帧未被破坏。
反之,接收站认为帧被破坏,则会通过一定的机制要求发送站重发该帧。
4.错检测
在校验字段中,使用的是CRC校验。
校验的范围包括目的地址字段、源地址字段、长度字段、LLC数据字段。
循环冗余编码(CRC)是一种重要的线性分组码、编码和解码方法,具有简单、检错和纠错能力强等特点,在通信领域广泛地用于实现差错控制。
CRC校验码的检错能力很强,不仅能检查出离散错误,还能检查出突发错误。
利用CRC进行检错的过程可简单描述如下:
在发送端根据要传送的k位二进制码序列,以一定的规则产生一个校验用的r位监督码(CRC码),附在原始信息的后边,构成一个新的二进制码序列(共k+r位),然后发送出去。
在接收端,根据信息码和CRC码之间所遵循的规则进行检验,以确定传送中是否出错。
这个规则在差错控制理论中称为“生成多项式”。
循环冗余校验码的特点:
(1)CRC校验码可检测出所有单个错误。
(2)CRC校验码可检测出所有奇数位错误。
(3)CRC校验码可检测出所有双位的错误。
(4)CRC校验码可检测出所有小于、等于校验位长度的突发错误。
(5)CRC校验码可以的概率检测出长度为(K+1)位的突发错误。
5.实现步骤
1
2
3
4
5
5.1前导符
前导符包括7字节的前发送码和1字节的帧起始定界符。
由于这些都是固定写法,因此添加起来非常简单。
对应方法如下。
publicstaticbooleanappendPrefix(){
//前导符中前7个字节是交替出现的1和0
for(inti=0;i<7;++i){
tmp.add(0XAA);
}
//前导符的第8个字节是约定好的0XAB
tmp.add(0XAB);
returntrue;
}
5.2目的地址及源地址
我们知道,目的机器的硬件地址是通过ARP协议得到的,这需要发送ARP请求包才能得到,为了简化程序的设计,突出本次设计的要点,因此将目的地址固定写成“FF:
FF:
FF:
FF:
FF:
FF”。
类似地,获得本机地址也需要通过一系列的函数调用,在程序中就直接写成已知的硬件地址“74:
E5:
0B:
7D:
CE:
3A”。
对应方法如下。
publicstaticbooleanappendAddress(){
//由于未显式要求目的地址,因此程序中将目的地址固定为FF:
FF:
FF:
FF:
FF:
FF
for(inti=0;i<6;++i){
tmp.add(0XFF);
}
//本机地址为74:
E5:
0B:
7D:
CE:
3A
tmp.add(0X74);
tmp.add(0XE5);
tmp.add(0X0B);
tmp.add(0X7D);
tmp.add(0XCE);
tmp.add(0X3A);
returntrue;
}
5.3长度及数据字段
对于发送端来说,必须先获得数据的长度,将长度字段添加到帧中之后才能再添加数据字段,这就限定了必须访问输入文件两次,第一次用于获得文件长度,第二次用于封装帧。
当然在进行CRC计算的时候有一些地方可以优化。
本程序的文件是通过FileInputStream进行读入,通过FileOutputStream进行输出。
5.4帧检验序列
计算FCS是本程序最困难的部分。
算法描述如下:
(1)输入需要检验的序列M,以及发送方与接收方约定好的除数P,并初始化余数R为0,若P有n位,则R有n–1位;
(2)对序列M中的每一位(记为b)进行步骤(3)。
结束后R即为FCS。
(3)将R左移1位,并将b添加到R的最低位,判断R的最高位,如果是0,则继续(3),如果是1,则将R和P进行按位与操作,结果保存到R中。
图5.4.1CRC流程图
相应的流程图如图5.4.1所示,其实现如下,其中curByte为一个字节,currentR为从开始到现在已经循环计算所得的余数。
publicstaticintnextR(intcurrentR,intcurByte){
intmask=0X80;
intcurBit=0;
for(inti=0;i<8;++i,mask>>=1){
//计算当前位,0或者1
curBit=(curByte&mask)==0?
0X0:
0X1;
//首先将余数左移1位,并把当前位添加到余数的最低位
currentR<<=1;
currentR+=curBit;
//判断余数最高位是否为0
//若为1,则将除数和余数进行异或操作,将结果保存为余数
if((currentR&MASK_HIGH)!
=0){
currentR=currentR^P;
}
}
returncurrentR;
}
6.源代码
importjava.io.File;
importjava.io.FileInputStream;
importjava.io.FileOutputStream;
importjava.io.IOException;
importjava.io.InputStream;
importjava.io.OutputStream;
importjava.util.LinkedList;
importjava.util.List;
publicclassframer{
publicstaticvoidmain(String[]args)throwsIOException{
if(args.length<2){
System.out.println("参数错误!
");
return;
}
FileInputStreamfin=null;
FileOutputStreamfout=null;
try{
fin=newFileInputStream(newFile(args[0]));
fout=newFileOutputStream(args[1]);
}catch(IOExceptione){
System.out.println("文件错误!
");
return;
}
//添加前导符
appendPrefix();
//添加目的地址和源地址
appendAddress();
//添加数据,包括数据长度、数据内容以及帧检验序列
appendData(fin,fout);
fin.close();
fout.close();
}
/**
*添加8个字节的前导符
*
*@return添加成功时返回true,否则false
*/
publicstaticbooleanappendPrefix(){
//前导符中前7个字节是交替出现的1和0
for(inti=0;i<7;++i){
tmp.add(0XAA);
}
//前导符的第8个字节是约定好的0XAB
tmp.add(0XAB);
returntrue;
}
/**
*添加6个字节的目的地址,和添加6个字节的源地址
*
*@return添加成功时返回true,否则false
*/
publicstaticbooleanappendAddress(){
//由于未显式要求目的地址,因此程序中将目的地址固定为FF:
FF:
FF:
FF:
FF:
FF
for(inti=0;i<6;++i){
tmp.add(0XFF);
}
//本机地址为74:
E5:
0B:
7D:
CE:
3A
tmp.add(0X74);
tmp.add(0XE5);
tmp.add(0X0B);
tmp.add(0X7D);
tmp.add(0XCE);
tmp.add(0X3A);
returntrue;
}
/**
*添加2个字节的数据长度,接着添加数据字段,最后添加帧检验序列FCS
*
*@paramin
*输入文件
*@paramout
*输出流
*@return添加成功时返回true,否则false
*/
publicstaticbooleanappendData(InputStreamin,OutputStreamout){
int[]data=newint[MAX_SIZE];
//文件长度
intsize=0;
//当前字节
intcurByte=0;
try{
while(size=-1){
data[size++]=curByte;
}
}catch(IOExceptione1){
e1.printStackTrace();
returnfalse;
}
//首先添加2个字节的数据长度
tmp.add(size>>8);
tmp.add(size);
//然后添加数据字段
for(inti=0;data[i]!
=-1&&itmp.add(data[i]);
}
//若数据长度不足,则补充填充字节
if(sizefor(inti=0;itmp.add(FILL_BYTE);
}
}
//计算FCS
//初始化余数为0
intR=0;
try{
for(intb:
tmp){
//每次读入一个字节,将它添加到帧中,并且循环计算FCS
out.write(b);
R=nextR(R,b);
}
}catch(IOExceptione){
e.printStackTrace();
returnfalse;
}
try{
//最后添加4个字节的帧检验序列
out.write(R>>24);
out.write(R>>16);
out.write(R>>8);
out.write(R);
}catch(IOExceptione){
e.printStackTrace();
returnfalse;
}
returntrue;
}
publicstaticintnextR(intcurrentR,intcurByte){
intmask=0X80;
intcurBit=0;
for(inti=0;i<8;++i,mask>>=1){
//计算当前位,0或者1
curBit=(curByte&mask)==0?
0X0:
0X1;
//首先将余数左移1位,并把当前位添加到余数的最低位
currentR<<=1;
currentR+=curBit;
//判断余数最高位是否为0
//若为1,则将除数和余数进行异或操作,将结果保存为余数
if((currentR&MASK_HIGH)!
=0){
currentR=currentR^P;
}
}
returncurrentR;
}
//数据字段的最大长度
publicstaticfinalintMAX_SIZE=1500;
//数据字段的最小长度
publicstaticfinalintMIN_SIZE=46;
//填充字节
publicstaticfinalintFILL_BYTE=0X00;
//发送方与接收方约定好的除数P
publicstaticfinalintP=0XACEF1357;
//用于判断余数最高位是否为1的掩码
publicstaticfinalintMASK_HIGH=0X8000;
privatestaticListtmp=newLinkedList();
}
7.运行结果示例
为了演示需要,首先新建一个文本文件,内容为“ChenJiaHui”(不含引号),如图7.1所示。
用文本文件的一个好处是可以方便的检测输出文件(即帧的格式)是否正确。
图7.1输入文件
接下来将源程序进行编译和运行。
命令“javac”表示将源代码编译成类文件。
命令“java”表示执行一个类文件,其后跟着的“input1input2”分别表示输入文件和输出文件,其中输入文件是所要包装的数据,即上文提到的文本文件,输出文件表示将帧封装好之后保存到哪个文件中,文件不存在时自动创建,存在时会覆盖。
如图7.2所示。
图7.2编译运行
由于实验只需要将数据封装成帧,并不要求检验,但如果不检验的话,又不知道是否封装正确,因此我另外写了一段程序,用以检验帧是否封装正确。
在图7.3中可以看到,这个类的名字为“check”,它接收一个参数,用以指明所要检验的帧,这里是“input2”,即由framer程序产生的帧文件。
由于input2是一个二进制文件,因此无法通过一般的编辑器将其打开以查看其中的内容。
图7.3检测输出文件
第一行有64位,前56位是前同步码,后8位是帧起始定界符。
第二行是目的地址。
因为没有显示指定目的地址,所以程序中将目的地址设为“FF:
FF:
FF:
FF:
FF:
FF”。
第三行是源地址。
源地址为本机的MAC地址。
第四行是长度,表示原输入文件的大小。
由于输入文件input1只含有“ChenJiahui”10个字符,因此该文件的大小的确是10.
接下来十行是源文件中的10个字节,为了避免输出过于冗余,所以只输出前10个字节,可以看到,每个字节均对应正确的字符,比如“1000011”表示字符“C”。
最后四行是4个字节的FCS。
以上输出中,有些字节不足8位,是因为均忽略了前导0。
8.心得体会
帧是数据链路层最重要的概念。
数据链路层属于计算机网络的低层。
我们知道,两个主机之间的数据传输,总是在一段一段的链路上传送的,也就是说,在两个相邻结点之间(主机和路由器或两个路由器之间)传送数据是直接传送的(点对点)。
这时就需要专门的链路层的协议。
在两个相邻结点之间传送数据时,数据链路层将网络层交下来的IP数据报组装成帧(framing),在两个相邻结点间的链路上“透明”地传送帧(frame)中的数据。
每一帧包括数据和必要的控制信息(如同步信息、地址信息、差错控制等)。
典型的帧长是几百字节到一千多字节。
在接收数据时,控制信息使接收端能够知道一个帧从哪个比特开始和到哪个比特结束。
这样,数据链路层收到一个帧后,就可从中提取出数据部分,上交给网络层。
控制信息还使接收端能够检测到所收到的帧中有无差错。
如发现有差错,数据链路层就简单地丢弃这个出现差错的帧,以免继续传送下去白白浪费网络资源。
在此次的课程设计中,刚开始两天我先是收集资料,查阅了很多帧的封装的知识,对帧及其封装的方法有了一定程度的了解和掌握。
从最开始对帧的一个模糊的印象到对帧的结构的完全掌握,有了很大的收获,并且使帧的封装得以实现。
同时,在近一周的课程设计中,我的动手能力得到了很大的提高,而且将这学期所学的网络的知识和以前所学的编程的知识充分的联系起来,对这门课的认识又提高了一层。
除此之外,在做课程设计的时候也遇到了很多的问题,最