重庆大学计算机网络课程设计4_计算机网络课程设计
重庆大学计算机网络课程设计4由刀豆文库小编整理,希望给你工作、学习、生活带来方便,猜你可能喜欢“计算机网络课程设计”。
《计算机网络》课程设计报告
学院:计算机学院
专业:计算机科学与技术6班
指导老师:宋伟
学生姓名:李皓楠、王博韬、魏征、杨俊铸
计算机网络项目书
一,设计目标
Project2:滑动窗口协议模拟
编写一个简单的滑动窗口协议模拟程序,实现两台主机间基于滑动窗口的流控机制。拓扑结构如下图所示:
主机A 传输模拟器器 主机B(1)主机A、主机B及传输模拟器可分别用线程模拟;
(2)传输模拟器用于实现网络传输过程中的丢包、错包和延迟;
(3)定义主机间传输的PDU的结构,包括发送方标识、接收方标识、帧序号、确认号等、窗口大小等,以用于实现Go-Back-N的滑动窗口协议;
(4)在两个主机间传送需要切分成多个帧的大数据块(可直接传输文件进行模拟);(5)一开始的时候,可以让网络仿真网关不丢弃任何帧,也不产生差错帧,检查程序是否能够正常完成传输,为了验证程序是否按照协议工作,让两个程序都显示收发包信息
(6)开启网络仿真网关的丢包功吧一定的丢包率进行丢包,重新运行两个应用程序,检查是否能正常完成文件的传输
(7)
开启网络仿真网关的错包功能,使其按照一定的错包率产生错包,重新运行两个应用程序,检查是否能正常完成文件的传输
(1)
开启网络仿真网关的延时功能,使其按照一定的概率产生延时,重新运行两个应用程序,检查是否能正常完成文件的传。
(2)能直观体现出收发双方窗口的滑动过程或Go-Back过程。
二,任务分配
1.组内分工
代码编写:王博韬、魏征
资料查找、程序测试、报告编写:李皓楠、杨俊铸
三,系统设计
1,基本原理
滑动口协议的基本原理就是在任意时刻,发送方都维持了一个连续的允许发送的帧的序号,称为发送窗口;同时,接收方也维持了一个连续的允许接收的帧的序号,称为接收窗口。发送窗口和接收窗口的序号的上下界不一定要一样,甚至大小也可以不同。不同的滑动窗口协议窗口大小一般不同。发送方窗口内的序号代表了那些已经被发送,但是还没有被确认的帧,或者是那些可以被发送的帧。接受方为其窗口内的每一个序号保留了一个缓冲区。与每个缓冲区相关联的还有一位,用来指明该缓冲区是满的还是空的。若从滑动窗口的观点来统一看待1比特滑动窗口、后退n及选择重传三种协议,它们的差别仅在于各自窗口尺寸的大小不同而已。1比特滑动窗口协议:发送窗口=1,接收窗口=1;后退N协议:发送窗口>1,接收窗口=1;选择重传协议:发送窗口>1,接收窗口>1。
四,系统实现
考虑设计采用进程模拟,主要算法写在check.h中,编写发送窗口与接收窗口调用实现。发送帧格式为 第一位表示接发者,(0表示数据帧,1表示确认帧)。第二至第四位表示 帧序号(确认号),数据帧由第5位开始到最后为数据与末3位的校验码,确认帧无校验码。
窗口大小为7,采用CRC校验(生成多项式为1010)
1,发送窗口主要函数实现
函数名:send_data2 功能:检测是否需要产生错误,检测是否需要丢包(只丢一次),将string类型转为字符数组,发送缓冲区所有数据,函数名:send_data 功能:发送数据块。
确认帧类型+帧序号+数据部分,加CRC校验码,加入缓冲池
函数名:accept_inf 功能:接受监测
2,接受窗口主要函数实现
函数名:send_ack 功能:确认并请求帧
函数名:data_handling 功能:数据处理
函数名:accept_inf 功能:接受线程信息,进行CRT校验,主要数据处理
3,数据处理部分主要函数实现
函数名:bin_division 功能:二进制除法(被除数与除数位数一致),返回余数(字符串形式)
函数名:bin_division_2 功能:二进制除法(被除数位数不定),返回字符串型余数
函数名:check 功能:校验帧是否发生错误,返回bool值
函数名:add_CRC 功能:为字符串型加上CRC校验码,返回字符串类型
函数名:cut_CRC 功能:为字符串类型剪掉CRC校验码,返回字符串类型
函数名:cut_header 功能:去掉数据头部
函数名:int_to_bin 功能:将帧序号转换为二进制
函数名:bin_to_int 功能:提取出帧序号
函数名:make_mistake 功能:产生错误(第6位取反)
函数名:get_ack 功能:获取ACK序号
五,运行结果
首先开始运行sever程序,界面:
打开cliect
首先检测不丢弃任何帧,也不产生差错帧,输入数据3,3,3空格隔开,运行
Sever界面收到的数据
再监测产生错误,输入数据3,7,7
接受界面
丢包:7.7.3
延时 7 3 7
六,总结
根据实验结果,可以看到延时,丢包和产生错误都成功发生。数据传输和接收同步进行,且产生错误不会阻断发送数据。经过试验过程,我们对滑动窗口协议有了更直观的认识。
七,代码部分 1,check.h #ifndef CHECK_H
//防止重编译
#define CHECK_H #endif
#include #include using std::string;
string bin_division(string dividend,string divisor){
//二进制除法(被除数与除数位数一致),返回余数(字符串形式)int length=dividend.length();string result =“”;if(dividend[0]=='0'){
divisor=“0000000000”;} for(int i=1;i
if(dividend[i]==divisor[i]){
result=result+'0';
}
else{
result=result+'1';
} } return result;}
string bin_division_2(string Dividend,string &Divisor){
返回字符串型余数
int length=Dividend.length();string s_temp=“”;for(int i=0;i
s_temp=s_temp+Dividend[i];} for(int i=3;i
s_temp=s_temp+Dividend[i];
s_temp=bin_division(s_temp,Divisor);} return s_temp;}
bool check(string Dividend,string Divisor){
返回bool值
if(bin_division_2(Dividend,Divisor)==“000”){
return true;} return false;}
string add_CRC(string bin_code,string Divisor){
为字符串型加上CRC校验码,返回字符串类型 string C_bin_code;string s_temp=“000”;
//二进制除法(被除数位数不定),//校验帧是否发生错误,//
s_temp=bin_code+s_temp;s_temp=bin_division_2(s_temp,Divisor);C_bin_code=bin_code+s_temp;return C_bin_code;}
string cut_CRC(string C_bin_code,string Divisor){
//为字符串类型剪掉CRC校验码,返回字符串类型
int length=C_bin_code.length();string bin_code=“”;for(int i=0;i
bin_code=bin_code+C_bin_code[i];} return bin_code;}
string cut_header(string s_temp){
去掉头部
string s_result=“”;int length=s_temp.length();for(int i=4;i
s_result=s_result+s_temp[i];} return s_result;}
string int_to_bin(int i_temp){
帧序号转换为二进制
string s_temp=“”;for(int i=0;i
if(i_temp%2==1){
s_temp='1'+s_temp;
}
else{
s_temp='0'+s_temp;
}
i_temp=i_temp/2;} return s_temp;}
int bin_to_int(string s_temp){
序号
int i_temp=0,i_temp2=4;
//
//将//提取出帧
} for(int i=1;i
i_temp+=i_temp2;} i_temp2=i_temp2/2;} return i_temp;string make_mistake(string s_temp){
//主动产生错误(第6位取反)
if(s_temp[5]=='0'){
s_temp[5]='1';} else{
s_temp[5]='0';} return s_temp;}
int get_ack(char receiveBuf[20]){
//获取ACK序号
int i_temp;i_temp=(int)(receiveBuf[6]-'0');return i_temp;}
2,server.cpp /* 发送帧格式为 第一位表示接发者,(0表示数据帧,1表示确认帧)。第二至第四位表示 帧序号(确认号),数据帧由第5位开始到最后为数据与末3位的校验码,确认帧无校验码。
窗口大小为7,采用CRC校验(生成多项式为1010)。
***11 */ #include
#include
//#include #pragma comment(lib,“ws2_32.lib”)//#include #include
#include #include “check.h” #include using std::ofstream;using std::cout;using std::endl;using std::string;static int buffer[8];
static string data_temp=“”;
//每次收到的数据块,字符串形式存储
const static string Divisor=“1010”;
//CRC校验,校验码为“1010”
static int ack =0;
//需要的帧序号
void send_ack(int ack,SOCKET &serConn){
认并请求帧
char c_temp[8]={'0','1','2','3','4','5','6','7'};string s_temp=“”;s_temp=s_temp+“请求第”+c_temp[ack]+“帧”;char sendBuf[20];strncpy(sendBuf,s_temp.c_str(),20);
send(serConn,sendBuf,strlen(sendBuf)+1,0);
return;}
void data_handling(string s_temp,SOCKET &serConn){
int frame=bin_to_int(s_temp);if(frame==ack){
s_temp=cut_header(s_temp);
data_temp=data_temp+s_temp;
cout
if(s_temp.length()
若帧长度小于17,为数据结束帧。
//打印最终数据
ofstream out_file(“temp_file_accept.txt”);
将数据存入文件
out_file
data_temp=“”;
//初始化数据块,为下一次数据接收做准备
cout
}
ack=(ack+1)%8;
//请求帧序号+1
send_ack(ack,serConn);
//数据处理
//确 //
//
if(ack==7){
//开始下一轮接收
ack=0;
} } return;}
void * accept_inf(void * arg){
监测
SOCKET serConn=*(SOCKET *)arg;
//套接字
int i_temp;
//临时整形数值
char receiveBuf[20];
//用于接收传输帧
string s_temp;
//临时存储文件,用于CRC校验
while(1){
i_temp=recv(serConn,receiveBuf,21,0);
//接收成功返回接收帧的字节长度,或者0,失败则为-1
if(i_temp>0){
s_temp=receiveBuf;
if(check(s_temp,Divisor)){
int order=bin_to_int(s_temp);
cout
s_temp=cut_CRC(s_temp,Divisor);
data_handling(s_temp,serConn);
//进行数据处理
}
else{
cout
}
}
} pthread_exit(0);
线程结束(此次实验不结束)。
}
int main(){ //创建套接字
//接收信息线程,被动
//
WORD myVersionRequest;
WSADATA wsaData;myVersionRequest=MAKEWORD(1,1);
//高(低)8位值,共16位
1.1版本库
int err;err=WSAStartup(myVersionRequest,&wsaData);if(!err){
cout
else{//进一步绑定套接字
cout
SOCKET serSocket=socket(AF_INET,SOCK_STREAM,0);//创建了可识别套接字
//使用IPV4,与TCP协议
//需要绑定的参数
SOCKADDR_IN addr;
addr.sin_family=AF_INET;
addr.sin_addr.S_un.S_addr=htonl(INADDR_ANY);
//ip地址,默认本地IP
addr.sin_port=htons(6000);//绑定端口
bind(serSocket,(SOCKADDR*)&addr,sizeof(SOCKADDR));//绑定完成listen(serSocket,5);//其中第二个参数代表能够接收的最多的连接数
//////////////////////////////////////////////////////////////////////////
//开始进行监听
//////////////////////////////////////////////////////////////////////////
SOCKADDR_IN clientsocket;
int len=sizeof(SOCKADDR);
SOCKET serConn=accept(serSocket,(SOCKADDR*)&clientsocket,&len);
//如果这里不是accept而是connection的话。就会不断的监听
////////////////////////获取到个连接者/////////////////////////////////////
char sendBuf[20]=“两地信道通畅”;
send(serConn,sendBuf,strlen(sendBuf)+1,0);
pthread_t id_accept,id_send;
//接收窗口.线程
pthread_create(&id_accept,NULL,accept_inf,&serConn);pthread_join(id_accept,NULL);closesocket(serConn);//关闭
WSACleanup();//释放资源的操作
return 0;}
3,client.cpp #include #include
//#include #pragma comment(lib,“ws2_32.lib”)#include #include “check.h” #include #include using std::cout;using std::endl;using std::string;using std::ifstream;using std::cin;static int ack_order;const static string Divisor=“1010”;
//CRC校验码
static int error=7;
//错误包
static int delay=2;static int abandon=7;static int accept_ack=0;
void send_data2(int i_First,int i_last,string buffer_temp[],SOCKET &clientSocket){ if(error
//检测是否需要产生错误
buffer_temp[error]=make_mistake(buffer_temp[error]);}
for(int j=i_First;j
//发送缓冲区所有数据
if(j==abandon || j==delay){
//检测是否需要丢包,只丢一次
abandon=7;
}
else{
char sendBuf[20];
//将string类型转为字符数组
strncpy(sendBuf,buffer_temp[j].c_str(),20);
cout
send(clientSocket,sendBuf,strlen(sendBuf)+1,0);
//发送
Sleep(200);
}
} if(delay
//是否需要延迟某包
char sendBuf[20];
//将string类型转为字符数组
strncpy(sendBuf,buffer_temp[delay].c_str(),20);
}
cout
send(clientSocket,sendBuf,strlen(sendBuf)+1,0);
//发送
Sleep(200);delay=7;if(error
//只产生一次错误
buffer_temp[error]=make_mistake(buffer_temp[error]);
error=7;}
return;}
void send_data(SOCKET &clientSocket){
//发送数据块,数据文档为“temp_file_send.txt”
ifstream in_file(“temp_file_send.txt”);int order;
//帧序号
string s_file,file_temp;char file,type='0';
//帧类型
string buffer[7];
//缓冲池
while(!in_file.eof()){
//保证文件所有内容均被读取
int i;
for(i=0;i
order=i;
s_file=“”;
分
for(int j=0;j
//最多10位作为帧的数据部 in_file.read(&file,1);s_file=s_file+file;}
file_temp =type+int_to_bin(order)+s_file;
//帧类型+帧序号+数据部分
file_temp =add_CRC(file_temp,Divisor);
//加CRC校验码
buffer[i]=file_temp;
//加入缓冲池
}
send_data2(0,i,buffer,clientSocket);
//发送缓冲文件
while(accept_ack
//等待ACK确认,若ACK确认帧序数大于窗口数,开始下一帧
send_data2(accept_ack,i,buffer,clientSocket);
}
} cout
void * accept_inf(void * arg){
//接收监测口
SOCKET clientSocket=*(SOCKET *)arg;
int i_temp;
char receiveBuf[20];
while(1){
i_temp=recv(clientSocket,receiveBuf,21,0);
if(i_temp>0){
accept_ack=get_ack(receiveBuf);
//提取接收帧序号
} } pthread_exit(0);
}
int main(){
//////////////////////////////////////////////连接服务器进程///////////////////////////////////////////////
int err;WORD versionRequired;
WSADATA wsaData;
versionRequired=MAKEWORD(1,1);
err=WSAStartup(versionRequired,&wsaData);//协议库的版本信息 if(!err){ cout
//开始连接
char receiveBuf[20];
recv(clientSocket,receiveBuf,21,0);
cout
//确认初始化
pthread_t id_accept;
//接收线程创建
pthread_create(&id_accept,NULL,accept_inf,&clientSocket);
while(1){
cout
cin>>error>>delay>>abandon;
send_data(clientSocket);
}
send_data(clientSocket);
据块
pthread_join(id_accept,NULL);
closesocket(clientSocket);
WSACleanup();
return 0;}
//发送数