数据链路层协议的设计与实现.docx
《数据链路层协议的设计与实现.docx》由会员分享,可在线阅读,更多相关《数据链路层协议的设计与实现.docx(13页珍藏版)》请在冰豆网上搜索。
数据链路层协议的设计与实现
计算机通信网络实验
数据链路层协议的设计与实现
学院:
__
__
__
20XX11月11日
一、实验目的
计算机网络的数据链路层协议保证通信双方在有差错的通信线路上进行无差错的数据传输,是计算机网络各层协议中通信控制功能最典型的一种协议。
本实验实现一个数据链路层协议的数据传送部分,目的在于更好地理解基本数据链路层协议的基本工作原理,掌握计算机网络协议的基本实现技术。
二、实验内容
使用C语言实现下面数据链路层协议:
1.分析和实现一个理想的链路层协议
2.对于前面实现的协议进行扩充,实现它的第一次改进,如何防止发方过快淹没收方。
3.对上一步再假设在不可靠的的链路上进行通信。
三、实验步骤
1.熟悉数据链路层协议的功能;
2.编写数据链路层协议的实现程序;
3.调试并运行自己编写的协议实现程序;
4.了解协议的工作轨迹,如出现异常情况,在实验报告中写出原因分析;
5.保留你实现的数据链路层协议,以备教师检查。
四、实验过程
1、程序功能及设计思路
功能概述:
用客户端/服务器模式代表A站、B站。
先由客户端输入服务器IP地址,发送SYN同步帧,告诉服务器准备接受。
客户端输入数据后,会进行CRC编码,再发送数据帧;服务器收到后,先进行校验,数据正确则发送ACK帧,客户端则发送下一帧数据;否则服务器发送NAK帧,客户端重新发送该数据。
CRC校验:
1)将收到的字符转为int型〔32位,并将其二进制码左移16位,存于data;
2)进行C=Remainder[∙D^L>/g],即CRC校验,得到校验位。
3)将校验位加在信息元后,组成24位的码字,存于要发送的数据帧dframe。
停等式ARQ协议:
Client:
1)置SN=0;
2)收到数据,将SN分配给该数据,如果没有收到,则等待;
3)存于要发送的数据帧中,发送给server;
4)如果从server收到确认帧,且RN>SN,则SN加1〔模2,返回2;如果收到NAK或RN=SN,则返回3,重传数据。
Server:
1)置RN=0;
2)从client收到一个SN=RN的帧,进行CRC校验检查,无错后输出,并置RN加1、发送ACK帧;否则发送NAK帧,请求重发。
2、C语言程序代码:
客户端Client:
//***********************client.c*****************************
#include//WINSOCKAPI的头文件,需要包含在项目中
#pragmacomment//WINSOCKAPI连接库文件
#include
#include
interr;
SOCKETsock;//用于服务器监听的Socket
SOCKADDR_INaddrSrv;//服务端地址
unsignedcharsendBuf[100];//发送缓存
charserverIp[20];//客户端ip地址
intsocklen=sizeof;//Socket的地址值的长度
intcf_len=sizeof;
structdataFrame//数据帧
{
intseq;//分段消息的序号
intSN;//发送序号
unsignedintdata[100];
intmsglen;//字符长度,采用长度计数的组帧技术
};
structconFrame//控制帧
{
intRN;//接收序号
chartype[3];
//表明帧的类型:
SYN同步、EOT送毕、ACK确认应答、NCK否定应答
};
structdataFramedframe;
structconFramecframe;
//************************初始化******************************
voidinitialization<>
{
WORDwVersionRequested;
WSADATAwsaData;
wVersionRequested=MAKEWORD<1,1>;//WinSocket1.1版本
err=WSAStartup;
//wsaData用来存储系统传回的关于WinSocket的资料
if!
=1||HIBYTE!
=1>{
WSACleanup<>;
}
return;
}
//************************计算CRC-16******************************
//基于32位系统,int型长度为4字节,CRC-16的生成多项式为g=D^16+D^15+D^2+1
voidcaculate_crc16
{
unsignedintdata=0;
inti,j;
//chars[32];//用于测试时显示二进制码
for{
data=*msg;
//itoa;//把整数转为二进制码
//printf<"第%d字的二进制码:
%s\n",i+1,s>;//test
*crc=data<<16;
for
{
if<>
{
data=&0xffff;
data=data^0x8005;
}
elsedata=&0xffff;
}
*crc=*crc|data;//把校验位放在信息元后面,存在一个int变量中
//itoa<*crc,s,2>;
//printf<"加上CRC校验位后的二进制码:
%s\n",s>;//test
}
}
//************************发送数据帧******************************
voidSendFrame<>{
//建立socket,SOCK_DGRAM为使用不连续不可靠的数据包连接
sock=socket;
if{
printf<"Buildingasocketfailed.\n">;
return;
}
printf<"inputserver'sIP:
">;
scanf<"%s",serverIp>;//输入服务器ip
addrSrv.sin_addr.S_un.S_addr=inet_addr;//设置服务器地址
addrSrv.sin_family=AF_INET;
addrSrv.sin_port=htons<6000>;//设置服务器端口号
strcpy;
cframe.RN=0;
//发送同步消息
sendto&cframe,sizeof+1,0,&addrSrv,socklen>;
printf<"----------------------------------------------\n">;
dframe.seq=0;
while<1>
{
memset>;//清空缓冲区
printf<"Inputthemessage:
">;//输入message,若输入exit则停止发送
scanf<"%s",sendBuf>;
if==0>{
//当客户端要断开连接时,给服务器发送EOT送毕控制帧
strcpy;
//发送EOT消息
sendto&cframe,sizeof+1,0,&addrSrv,socklen>;
printf<"------------------closesocket!
-----------------\n">;
break;
}
dframe.seq++;
dframe.SN=dframe.seq%2;
dframe.msglen=strlen;
caculate_crc16;
//发送消息
sendto&dframe,sizeof,0,&addrSrv,socklen>;
while<1>
{
recvfrom&cframe,sizeof,0,&addrSrv,&cf_len>;//接受来自服务器的应答帧
if==0>//如果收到ACK应答指令,则发送下一个message
{
if=dframe.SN>{
printf<"--------messageisdeliveredsuccessfully.----------\n">;
break;
}
}elseif<==0>|>
{
printf<"--------messageredeliverring.----------\n">;
sendto&dframe,sizeof,0,&addrSrv,socklen>;//重发该消息
}
}
}
closesocket;//关闭连接
return;
}
voidmain<>{
initialization<>;//初始化阶段,若返回值err=0,则表示初始化成功
if{
printf<"Initializationfalied.\n">;
exit<0>;
}
SendFrame<>;
WSACleanup<>;
}
//**********************endofprogram************************
服务器Server:
//***********************server.c*****************************
#include//WINSOCKAPI的头文件,需要包含在项目中
#pragmacomment//WINSOCKAPI连接库文件
#include
#include
interr;
SOCKETsock;//用于服务器监听的Socket
SOCKADDR_INaddrSrv;//服务端地址
SOCKADDR_INaddrClient;//客户端地址
unsignedcharrecvBuf[100];//接受缓存
intcf_len=sizeof;//实际存储在recvBuf的地址的长度
intsocklen=sizeof;//Socket的地址值的长度
structdataFrame//数据帧
{
intseq;//分段消息的序号
intSN;//发送序号
unsignedintdata[100];
intmsglen;//字符长度,采用长度计数的组帧技术
};
structconFrame//控制帧
{
intRN;//接收序号
chartype[3];
//表明帧的类型:
SYN同步、EOT送毕、ACK确认应答、NCK否定应答
};
structdataFramedframe;
structconFramecframe;
//************************初始化******************************
voidinitialization<>
{
WORDwVersionRequested;
WSADATAwsaData;
interr;
wVersionRequested=MAKEWORD<1,1>;//WinSocket1.1版本
err=WSAStartup;
//wsaData用来存储系统传回的关于WinSocket的资料
if!
=1||HIBYTE!
=1>{
WSACleanup<>;
}
return;
}
//************************绑定端口******************************
voidbindport<>
{
addrSrv.sin_addr.S_un.S_addr=htonl;
//设置服务器地址,INADDR_ANY表示使用自己的IP地址
addrSrv.sin_family=AF_INET;
addrSrv.sin_port=htons<6000>;//设定端口为6000
err=bind&addrSrv,sizeof>;
return;
}
//**********************CRC校验**************************
voidcheckout
{
inti,j;
unsignedinttemp;
unsignedintdata;
unsignedchar*msg=recvBuf;//*msg指向recvBuf的首地址
//unsignedchars[32];//用于测试时显示二进制码
for
{
data=*pdata;
temp=>>16;//将校验位清零,使信息位移到低8位
*msg=temp;
//itoa;//把整数转为二进制码//test
//printf<"收到的二进制码:
%s\n",s>;//test
for//有16位校验元,故循环操作16次,使得到Remainder[R/g]
{
if
{
data=&0xffffff;
data=data^0x800500;
}
elsedata=&0xffffff;
}
if{
strcpy;
//若Remainder[R/g]=0,表示接收序列无误
}
else{
strcpy;
//若Remainder[R/g]!
=0,表示接收序列有误
return;
}
pdata++;
}
return;
}
//**********************发送及接受消息**************************
voidRecvFrame<>
{
printf<"--------------------serverwaitting.--------------------\n">;
while<1>
{
recvfrom&cframe,sizeof,0,&addrClient,&cf_len>;//接受来自客户端的同步帧
if==0>{
printf<"---------connectwith%s.-------\n",inet_ntoa>;
break;
}
}
while<1>
{
cf_len=sizeof;
memset>;//清空接收缓冲区
err=recvfrom&dframe,sizeof,0,&addrClient,&cf_len>;//接受来自客户端的数据帧
if{
//当客户端关闭socket时,发送过来的数据会使err=9,则服务器也关闭socket
printf<"---------clientclosethesocket!
-------\n">;
break;
}
checkout;
//调用checkout<>函数,CRC检验接收消息是否正确
if==0>
{
printf<"client:
%s\n",recvBuf>;
printf<"---------messageiscorrect!
-------\n">;
cframe.RN=%2;
}
elseif==0>
{
cframe.RN=dframe.SN;
printf<"----------messageiswrong!
-------\n">;
}
sendto&cframe,sizeof,0,&addrClient,socklen>;
//发送ACK帧或NAK帧
}
printf<"--------------------Theend!
------------------\n">;
return;
}
voidmain<>
{
initialization<>;//初始化阶段,若返回值err=0,则表示初始化成功
if=0>{
printf<"Intializationfailed.\n">;
return;
}
sock=socket;
//建立socket,SOCK_DGRAM为使用不连续不可靠的数据包连接
if{
printf<"Buildingasocketfailed.\n">;
return;
}
bindport<>;//绑定端口
if=0>{
printf<"Bindingasocketfailed.\n">;
return;
}
RecvFrame<>;//接受客户端发送数据
closesocket;//关闭连接
WSACleanup<>;
}
//**********************endofprogram************************
3、实验结果
测试1:
在没有传输错误情况下,在dos界面显示的传递内容的二进制码和CRC校验码
Client:
222.25.162.196,发送数据"yl",显示"messageisdeliveredsuccessfully"后关闭连接。
Server:
222.25.162.196,收到数据"yl",与上图对比,码字正确,则正确输出数据。
测试2:
在传输出错情况下,在dos界面显示传递内容的二进制码和CRC校验码
Client:
222.25.162.196,发送数据"yl",传输出错收到NAK后重传数据。
Server:
222.25.162.196,与上图的二进制码对比发现,第2个码字最后1一位出错,
程序显示"messageiswrong",然后等待客户端重传。
通信测试:
在dos界面进行通信,但隐藏了二进制码、校验码,实现的是数据的透明传输。
Client:
Server:
222.25.162.196
分析:
从上面两图可以看出,服务端和客户端可以实现通畅且准确无误的通信,基本能够实现预期功能。
五、实验心得
通过本次实验,对数据链路层中数据帧的传输和CRC校验有了更深的了解,不仅仅是停留在书本的理论上了,而且自己对网络编程也有了更大的兴趣。