专业班号____________________ 姓 名____________________ 实验日期____________________ 指导教师____________________ 第________实验 第_______组 实验名称 ______________________________ ARQ协议实现
一、 实验目的
1.实现一个简单的自动重发协议的模拟演示 2.了解LLC层网络协议的实现方法 3.掌握MFC中的系统计时器
4.初步掌握MFC的体系结构以及消息映射方法
5.掌握VC++中AppWizard和ClassWizard的基本使用方法。 6.了解LLC层部分协议的实现步骤。 7.了解模拟的简单的重发协议的流程。 8.理解对缓冲区与滑动窗口的概念。
二、 实验环境
1.开发语言 Visual C++ 6.0 2. 软件平台 Windows XP
三、 实验原理
1、 协议及网络状态的假设
(1) 发送方将一帧发出以及帧通过物理链路传送到接收方的总时间为tl; (2) 接收方对该帧进行处理的时间为tp; (3) 接收方发ACK或NAK帧的时间为ts;
(4) 发送方收到ACK或NAK帧,进行处理的时间为tr; (5) 重发计时器的过期时间为timeout;
(6) 物理链路存在一定的丢失率以及错误率; (7) 缓冲区窗口大小为1,采用1位编码 2、发送方发送
(1) vs设0;
(2) 从缓冲区读帧,如缓冲区为空,则发送结束; (3) 发送;
(4) 设超时计数器; (5) 等待;
(6) 收到ACK,vs转成下帧的序列号,清除缓冲区,缓冲区置下帧,转(2); (7) 收到NAK,转(3); (8) 计时器超时,转(3); 3、接收方接收
(1) vr设0;
(2) 等待;
(3) 收到数据帧,正确则继续,出错则转(8); (4) 帧号对时继续;否则丢弃当前帧,转(2) (5) 将帧送网络层;
(6) Vr转成下一帧序号; (7) 发送确认帧ACK,转(2); (8) 发送NAK,转(2)
四、 实验步骤
1. 新建工程,选择
MFC AppWizard[exe]
2。单击工作区面板上的Resources,放置各种需要的控件,右击控件,选择属性,可设置控件的ID和样式。
3.对前面命名的六个资源进行属性的设置,如下表所示,选中资源->单击右键->属性->样式
资源ID
IDC_EVE_SEND
类型 EDIT BOX
样式
Multiline选中
Vertical Scroll选中 Border选中 ReadOnly选中 Static Edge选中 Multiline选中
Vertical Scroll选中 Border选中 ReadOnly选中 Static Edge选中 Border选中
Static Edge选中 Number选中 Border选中
Want Return选中 Static Edge选中
资源对应变量 CString
m_eveSend
说明
发送方的事件将显示在这里
IDC_EVE_RECV EDIT BOX
CString
m_eveRecv 接收方的事件将显示在这里
IDC_BUFF IDC_TEXT STATIC TEXT
EDIT BOX
CString m_buff int m_size
缓冲区内容将显示在这里
得到用户需要发的帧个数
IDC_SEND IDC_SETTING
BUTTON BUTTON
CButton m_send CButton m_setting
发送n个帧进行演示
配置网络
4.用ClassWizard生成的方法添加成员变量,方法如下,选择“查看”->建立类向导->Member Variables
5.在资源面板中绘制新对话框IDD_SETTING,将对话框中样式的系统菜单选项不选中,如下图
双击对话框,ClassWizard将提示需要为该对话框资源制定一个类,选择Create a new class, 并取名为DlgSetting, 并按下图,表添加成员变量,并进行初始值设定
资源ID IDC_ERROR IDC_MISSING IDC_LATANCY IDC_TIMEOUT IDC_TP IDC_TS IDC_TR IDC_OK
类型 EDIT BOX EDIT BOX EDIT BOX EDIT BOX BUTTON EDIT BOX EDIT BOX Button
资源对应变量 int m_error int m_missing int m_tl
int m_timeout int m_tp int m_ts int m_tr
CButton m_ok
说明
帧出错率 帧丢失率 发送时延(ms)
重发计时计时器过时期限(ms)
接收方处理时间(ms) 处理ACK,NAK时间(ms) 发送ACK,NAK时间(ms) 设置完成
在DlgSetting.cpp的构造函数中加初始设定: DlgSetting::DlgSetting(CWnd* pParent /*=NULL*/) : CDialog(DlgSetting::IDD, pParent) { //{{AFX_DATA_INIT(DlgSetting)
// NOTE: the ClassWizard will add member initialization here m_error = 10; m_tl = 100; m_missing = 10; m_timeout =300; m_tp = 10; m_tr = 10; m_ts = 10; //}}AFX_DATA_INIT }
6.接下来进行具体的实现,首先是模拟网络的设置,在Setting对话框资源面板中双击OK按钮,添加单击事务,实现函数OnOk()
void DlgSetting::OnOK() { // TODO: Add extra validation here UpdateData(); if(m_error>100||m_error100||m_missing
7.整个设置对话框设置好了,接下来让它生效,在ARQDlg.h的开始处添加代码:
#if !defined(AFX_ARQDLG_H__7F277C47_32BA_4297_8A25_6D20F43064B9__INCLUDED_)
#define
AFX_ARQDLG_H__7F277C47_32BA_4297_8A25_6D20F43064B9__INCLUDED_
#include "DlgSetting.h" #if _MSC_VER > 1000 #pragma once
#endif // _MSC_VER > 1000
在ARQDlg.h中添加public变量DlgSetting setting: class CARQDlg : public CDialog {
// Construction public: DlgSetting setting; CARQDlg(CWnd* pParent = NULL); // standard constructor
工程初始化的时候要调用对话框进行设置,在ARQDlg.cpp中的OnInitDlg函数中添加代码如下:
// TODO: Add extra initialization here setting.DoModal(); return TRUE; // return TRUE unless you set the focus to a control
为了在演示过程中,也可以进行配置,双击按钮设置,添加单击事件代码: void CARQDlg::OnSetting(){ // TODO: Add your control notification handler code here setting.DoModal(); }
8.定义演示协议所需要用到的变量和函数。在CARQDlg中添加public成员变量以及函数声明:
class CARQDlg : public CDialog {
// Construction public: DlgSetting setting;//设置这个变量来设置和保存网络设置17数据 int vs;//发送帧号 int vr;//接收帧号 int queue;//队列长度 int time;//发送时间 int curr;//当前发送帧 CARQDlg(CWnd* pParent = NULL); // standard constructor //发送方处理函数 void ToBuffer();//将帧读入缓冲区 void SendFrame();//将缓冲区中的帧发送 void OnACK();//接收到ACK时的操作
void OnNAK();//接收到NAK时的操作 void OnTimeOut();//计时器超时时的操作 //接收方处理函数 void OnRecv();//接收到数据帧时候的操作 void SendACK();//发送ACK void SendNAK();//发送NAK
9.初始化状态变量,设定随机种子,在ARQDlg.cpp中的函数OnInitDlg中添加: // TODO: Add extra initialization here setting.DoModal(); SYSTEMTIME t; GetSystemTime(&t); srand(t.wMilliseconds); vs=0; vr=0; time=0; curr=0; queue=0; return TRUE; // return TRUE unless you set the focus to a control }
10.利用输入消息处理函数OnTimer实现函数间的调用,发时时间事件消息WM_TIMER
① 右键单击资源面板的文件夹,选择资源符号 ② 添加符号WM_ONACK、WM_ONNAK、WM_RECEIVE、WM_SENDACK、
WM_SENDFRAME、WM_SENDNAK、WM_TIMEOUT、WM_TOBUFFER.这些符号的值将由ClassWizard自动选择未用过的符号生成
③ 接下来用ClassWizard添加事件
WM_TIMER
④ 在Class Wizard生成的函数OnTimer中添加如下代码: void CARQDlg::OnTimer(UINT nIDEvent) { // TODO: Add your message handler code here and/or call default KillTimer(nIDEvent); switch(nIDEvent) { case WM_TIMEOUT: OnTimeOut(); break; case WM_TOBUFFER: ToBuffer(); break; case WM_SENDFRAME: SendFrame(); break;
case WM_RECEIVE: OnRecv(); break; case WM_ONACK: OnACK(); break; case WM_ONNAK: OnNAK(); break;
case WM_SENDACK: SendACK(); break; case WM_SENDNAK: SendNAK(); break; default: break; } CDialog::OnTimer(nIDEvent); }
11.在对话框资源中双击发送,产生函数OnSend并添加代码 void CARQDlg::OnSend()
{ // TODO: Add your control notification handler code here UpdateData(true); CString text; text.Format("%d个帧被送入队列\r\n",m_size); m_eveSend=text; queue=m_size; text.Format("现在队列中有%d个帧\r\n",queue); m_eveSend+=text; m_eveRecv=""; vs=vr=0; curr=0; time=0; ToBuffer(); UpdateData(false); }
12.最后实现前面定义过的函数,在ARQDlg.cpp中添加代码如下: void CARQDlg::ToBuffer() //将帧读入缓冲区 { if (queue==0) //已经全部发完 { CString text; text.Format("发送完成\r\n"); m_eveSend+=text; text.Format("发送完成,共发%d个帧,用时%dms\r\n",curr,time); m_buff=text; MessageBox(text); text.Format("平均用时%fms/帧",(float)time/(float)curr); m_buff+=text; MessageBox(text); UpdateData(false);
time=0; //重新置0
curr=0; //
}
else // 还没发完
{
curr++; //得到下面一帧
queue--;
CString text;
text.Format("当前缓冲区为第%d帧,还有%d帧要发\r\n",curr,queue);
m_buff=text;
UpdateData(false);
SendFrame(); //发送下面一帧
}
}
void CARQDlg::SendFrame() //将缓冲区中的帧发送
{
CString text;
text.Format("发送第%d帧,帧序号为%d\r\n",curr,vs);
m_eveSend+=text;
UpdateData(false);
SetTimer(WM_TIMEOUT,setting.m_timeout,NULL);//开始计时
int p=rand();
if(p%100>setting.m_missing) //So lucky!没有丢失
{
time+=setting.m_tl; //加上发送延时
SetTimer(WM_RECEIVE,setting.m_tl,NULL); //接收方收到
}
}
void CARQDlg::OnACK() //接收到ACK时的操作
{
CString text;
text.Format("接收到ACK帧,准备发下一帧\r\n");
m_eveSend +=text;
time +=setting.m_tr; //加上处理时间
vs=(vs+1)%2; //转变vs号
KillTimer(WM_TIMEOUT); //注销计时器
UpdateData(false);
SetTimer(WM_TOBUFFER,setting.m_tr,NULL);
}
void CARQDlg::OnNAK()
{
CString text;
text.Format("接收到NAK帧,准备重发\r\n");
m_eveSend+=text;
time +=setting.m_tr;
SetTimer(WM_SENDFRAME,setting.m_tr,NULL);
KillTimer(WM_TIMEOUT);
SendFrame();
UpdateData(false);
}
void CARQDlg::OnTimeOut()
{
CString text;
text.Format("计时器超时,准备重发\r\n");
m_eveSend+=text;
SendFrame();
UpdateData(false);
}
void CARQDlg::OnRecv() //接收到数据帧时候的操作
{
CString text;
text.Format("接收到第%d帧,帧序号为%d\r\n",curr,vs);
m_eveRecv+=text;
int p=rand();
if(p%100>setting.m_error&&vs==vr) //So lucky!没有损坏
{
text.Format("很幸运,%d帧完好无缺,被送往网络层\r\n",curr);
m_eveRecv+=text;
time+=setting.m_tp;
vr=(vr+1)%2;
SetTimer(WM_SENDACK,setting.m_tp,NULL);
}
else
{
if(vs==vr) //损坏,则发送NAK
{
text.Format("第%d帧损坏 \r\n",curr);
m_eveRecv+=text;
time+=setting.m_tp;
SetTimer(WM_SENDNAK,setting.m_tp,NULL); //发送NAK
}
else //序列号错误, 发ACK
{
text.Format("错误的序列号%d,该帧被丢弃 \r\n",vs);
m_eveRecv+=text;
time+=setting.m_tp;
SetTimer(WM_SENDACK,setting.m_tp,NULL); // 发送ACK
}
}
UpdateData(false);
}
void CARQDlg::SendACK()
{
CString text;
text.Format("发送ACK\r\n");
m_eveRecv+=text;
time+=setting.m_ts;
int p=rand();
if(p%100>setting.m_missing)
{
time+=setting.m_tl;
SetTimer(WM_ONACK,setting.m_tl,NULL);
}
else
{
text.Format("ACK不幸丢失 \r\n");
m_eveRecv+=text;
}
UpdateData(false);
}
void CARQDlg::SendNAK()
{
CString text;
text.Format("发送NAK\r\n");
m_eveRecv+=text;
int p=rand();
if(p%100>setting.m_missing)
{
time+=setting.m_tl;
SetTimer(WM_ONNAK,setting.m_tl,NULL);
}
else
{
text.Format("NAK不幸丢失 \r\n");
m_eveRecv+=text;
}
UpdateData(false);
}
13.编译调试,输入20,可能会得到下图结果,(根据随机数不同而不同
)
五、 实验总结
1. 通过4次试验,加上平时的练习,实验达到实验目的;
2. 通过实现协议,加深了对协议的理解;
3. 对c++6.0的功能有了进一步的认识,感觉挺好用的;
4. 当然我没学过c++,实验代码都是老师给的,函数名字都不敢改,我就严格用老师提供的ID名。
专业班号____________________ 姓 名____________________ 实验日期____________________ 指导教师____________________ 第________实验 第_______组 实验名称 ______________________________ ARQ协议实现
一、 实验目的
1.实现一个简单的自动重发协议的模拟演示 2.了解LLC层网络协议的实现方法 3.掌握MFC中的系统计时器
4.初步掌握MFC的体系结构以及消息映射方法
5.掌握VC++中AppWizard和ClassWizard的基本使用方法。 6.了解LLC层部分协议的实现步骤。 7.了解模拟的简单的重发协议的流程。 8.理解对缓冲区与滑动窗口的概念。
二、 实验环境
1.开发语言 Visual C++ 6.0 2. 软件平台 Windows XP
三、 实验原理
1、 协议及网络状态的假设
(1) 发送方将一帧发出以及帧通过物理链路传送到接收方的总时间为tl; (2) 接收方对该帧进行处理的时间为tp; (3) 接收方发ACK或NAK帧的时间为ts;
(4) 发送方收到ACK或NAK帧,进行处理的时间为tr; (5) 重发计时器的过期时间为timeout;
(6) 物理链路存在一定的丢失率以及错误率; (7) 缓冲区窗口大小为1,采用1位编码 2、发送方发送
(1) vs设0;
(2) 从缓冲区读帧,如缓冲区为空,则发送结束; (3) 发送;
(4) 设超时计数器; (5) 等待;
(6) 收到ACK,vs转成下帧的序列号,清除缓冲区,缓冲区置下帧,转(2); (7) 收到NAK,转(3); (8) 计时器超时,转(3); 3、接收方接收
(1) vr设0;
(2) 等待;
(3) 收到数据帧,正确则继续,出错则转(8); (4) 帧号对时继续;否则丢弃当前帧,转(2) (5) 将帧送网络层;
(6) Vr转成下一帧序号; (7) 发送确认帧ACK,转(2); (8) 发送NAK,转(2)
四、 实验步骤
1. 新建工程,选择
MFC AppWizard[exe]
2。单击工作区面板上的Resources,放置各种需要的控件,右击控件,选择属性,可设置控件的ID和样式。
3.对前面命名的六个资源进行属性的设置,如下表所示,选中资源->单击右键->属性->样式
资源ID
IDC_EVE_SEND
类型 EDIT BOX
样式
Multiline选中
Vertical Scroll选中 Border选中 ReadOnly选中 Static Edge选中 Multiline选中
Vertical Scroll选中 Border选中 ReadOnly选中 Static Edge选中 Border选中
Static Edge选中 Number选中 Border选中
Want Return选中 Static Edge选中
资源对应变量 CString
m_eveSend
说明
发送方的事件将显示在这里
IDC_EVE_RECV EDIT BOX
CString
m_eveRecv 接收方的事件将显示在这里
IDC_BUFF IDC_TEXT STATIC TEXT
EDIT BOX
CString m_buff int m_size
缓冲区内容将显示在这里
得到用户需要发的帧个数
IDC_SEND IDC_SETTING
BUTTON BUTTON
CButton m_send CButton m_setting
发送n个帧进行演示
配置网络
4.用ClassWizard生成的方法添加成员变量,方法如下,选择“查看”->建立类向导->Member Variables
5.在资源面板中绘制新对话框IDD_SETTING,将对话框中样式的系统菜单选项不选中,如下图
双击对话框,ClassWizard将提示需要为该对话框资源制定一个类,选择Create a new class, 并取名为DlgSetting, 并按下图,表添加成员变量,并进行初始值设定
资源ID IDC_ERROR IDC_MISSING IDC_LATANCY IDC_TIMEOUT IDC_TP IDC_TS IDC_TR IDC_OK
类型 EDIT BOX EDIT BOX EDIT BOX EDIT BOX BUTTON EDIT BOX EDIT BOX Button
资源对应变量 int m_error int m_missing int m_tl
int m_timeout int m_tp int m_ts int m_tr
CButton m_ok
说明
帧出错率 帧丢失率 发送时延(ms)
重发计时计时器过时期限(ms)
接收方处理时间(ms) 处理ACK,NAK时间(ms) 发送ACK,NAK时间(ms) 设置完成
在DlgSetting.cpp的构造函数中加初始设定: DlgSetting::DlgSetting(CWnd* pParent /*=NULL*/) : CDialog(DlgSetting::IDD, pParent) { //{{AFX_DATA_INIT(DlgSetting)
// NOTE: the ClassWizard will add member initialization here m_error = 10; m_tl = 100; m_missing = 10; m_timeout =300; m_tp = 10; m_tr = 10; m_ts = 10; //}}AFX_DATA_INIT }
6.接下来进行具体的实现,首先是模拟网络的设置,在Setting对话框资源面板中双击OK按钮,添加单击事务,实现函数OnOk()
void DlgSetting::OnOK() { // TODO: Add extra validation here UpdateData(); if(m_error>100||m_error100||m_missing
7.整个设置对话框设置好了,接下来让它生效,在ARQDlg.h的开始处添加代码:
#if !defined(AFX_ARQDLG_H__7F277C47_32BA_4297_8A25_6D20F43064B9__INCLUDED_)
#define
AFX_ARQDLG_H__7F277C47_32BA_4297_8A25_6D20F43064B9__INCLUDED_
#include "DlgSetting.h" #if _MSC_VER > 1000 #pragma once
#endif // _MSC_VER > 1000
在ARQDlg.h中添加public变量DlgSetting setting: class CARQDlg : public CDialog {
// Construction public: DlgSetting setting; CARQDlg(CWnd* pParent = NULL); // standard constructor
工程初始化的时候要调用对话框进行设置,在ARQDlg.cpp中的OnInitDlg函数中添加代码如下:
// TODO: Add extra initialization here setting.DoModal(); return TRUE; // return TRUE unless you set the focus to a control
为了在演示过程中,也可以进行配置,双击按钮设置,添加单击事件代码: void CARQDlg::OnSetting(){ // TODO: Add your control notification handler code here setting.DoModal(); }
8.定义演示协议所需要用到的变量和函数。在CARQDlg中添加public成员变量以及函数声明:
class CARQDlg : public CDialog {
// Construction public: DlgSetting setting;//设置这个变量来设置和保存网络设置17数据 int vs;//发送帧号 int vr;//接收帧号 int queue;//队列长度 int time;//发送时间 int curr;//当前发送帧 CARQDlg(CWnd* pParent = NULL); // standard constructor //发送方处理函数 void ToBuffer();//将帧读入缓冲区 void SendFrame();//将缓冲区中的帧发送 void OnACK();//接收到ACK时的操作
void OnNAK();//接收到NAK时的操作 void OnTimeOut();//计时器超时时的操作 //接收方处理函数 void OnRecv();//接收到数据帧时候的操作 void SendACK();//发送ACK void SendNAK();//发送NAK
9.初始化状态变量,设定随机种子,在ARQDlg.cpp中的函数OnInitDlg中添加: // TODO: Add extra initialization here setting.DoModal(); SYSTEMTIME t; GetSystemTime(&t); srand(t.wMilliseconds); vs=0; vr=0; time=0; curr=0; queue=0; return TRUE; // return TRUE unless you set the focus to a control }
10.利用输入消息处理函数OnTimer实现函数间的调用,发时时间事件消息WM_TIMER
① 右键单击资源面板的文件夹,选择资源符号 ② 添加符号WM_ONACK、WM_ONNAK、WM_RECEIVE、WM_SENDACK、
WM_SENDFRAME、WM_SENDNAK、WM_TIMEOUT、WM_TOBUFFER.这些符号的值将由ClassWizard自动选择未用过的符号生成
③ 接下来用ClassWizard添加事件
WM_TIMER
④ 在Class Wizard生成的函数OnTimer中添加如下代码: void CARQDlg::OnTimer(UINT nIDEvent) { // TODO: Add your message handler code here and/or call default KillTimer(nIDEvent); switch(nIDEvent) { case WM_TIMEOUT: OnTimeOut(); break; case WM_TOBUFFER: ToBuffer(); break; case WM_SENDFRAME: SendFrame(); break;
case WM_RECEIVE: OnRecv(); break; case WM_ONACK: OnACK(); break; case WM_ONNAK: OnNAK(); break;
case WM_SENDACK: SendACK(); break; case WM_SENDNAK: SendNAK(); break; default: break; } CDialog::OnTimer(nIDEvent); }
11.在对话框资源中双击发送,产生函数OnSend并添加代码 void CARQDlg::OnSend()
{ // TODO: Add your control notification handler code here UpdateData(true); CString text; text.Format("%d个帧被送入队列\r\n",m_size); m_eveSend=text; queue=m_size; text.Format("现在队列中有%d个帧\r\n",queue); m_eveSend+=text; m_eveRecv=""; vs=vr=0; curr=0; time=0; ToBuffer(); UpdateData(false); }
12.最后实现前面定义过的函数,在ARQDlg.cpp中添加代码如下: void CARQDlg::ToBuffer() //将帧读入缓冲区 { if (queue==0) //已经全部发完 { CString text; text.Format("发送完成\r\n"); m_eveSend+=text; text.Format("发送完成,共发%d个帧,用时%dms\r\n",curr,time); m_buff=text; MessageBox(text); text.Format("平均用时%fms/帧",(float)time/(float)curr); m_buff+=text; MessageBox(text); UpdateData(false);
time=0; //重新置0
curr=0; //
}
else // 还没发完
{
curr++; //得到下面一帧
queue--;
CString text;
text.Format("当前缓冲区为第%d帧,还有%d帧要发\r\n",curr,queue);
m_buff=text;
UpdateData(false);
SendFrame(); //发送下面一帧
}
}
void CARQDlg::SendFrame() //将缓冲区中的帧发送
{
CString text;
text.Format("发送第%d帧,帧序号为%d\r\n",curr,vs);
m_eveSend+=text;
UpdateData(false);
SetTimer(WM_TIMEOUT,setting.m_timeout,NULL);//开始计时
int p=rand();
if(p%100>setting.m_missing) //So lucky!没有丢失
{
time+=setting.m_tl; //加上发送延时
SetTimer(WM_RECEIVE,setting.m_tl,NULL); //接收方收到
}
}
void CARQDlg::OnACK() //接收到ACK时的操作
{
CString text;
text.Format("接收到ACK帧,准备发下一帧\r\n");
m_eveSend +=text;
time +=setting.m_tr; //加上处理时间
vs=(vs+1)%2; //转变vs号
KillTimer(WM_TIMEOUT); //注销计时器
UpdateData(false);
SetTimer(WM_TOBUFFER,setting.m_tr,NULL);
}
void CARQDlg::OnNAK()
{
CString text;
text.Format("接收到NAK帧,准备重发\r\n");
m_eveSend+=text;
time +=setting.m_tr;
SetTimer(WM_SENDFRAME,setting.m_tr,NULL);
KillTimer(WM_TIMEOUT);
SendFrame();
UpdateData(false);
}
void CARQDlg::OnTimeOut()
{
CString text;
text.Format("计时器超时,准备重发\r\n");
m_eveSend+=text;
SendFrame();
UpdateData(false);
}
void CARQDlg::OnRecv() //接收到数据帧时候的操作
{
CString text;
text.Format("接收到第%d帧,帧序号为%d\r\n",curr,vs);
m_eveRecv+=text;
int p=rand();
if(p%100>setting.m_error&&vs==vr) //So lucky!没有损坏
{
text.Format("很幸运,%d帧完好无缺,被送往网络层\r\n",curr);
m_eveRecv+=text;
time+=setting.m_tp;
vr=(vr+1)%2;
SetTimer(WM_SENDACK,setting.m_tp,NULL);
}
else
{
if(vs==vr) //损坏,则发送NAK
{
text.Format("第%d帧损坏 \r\n",curr);
m_eveRecv+=text;
time+=setting.m_tp;
SetTimer(WM_SENDNAK,setting.m_tp,NULL); //发送NAK
}
else //序列号错误, 发ACK
{
text.Format("错误的序列号%d,该帧被丢弃 \r\n",vs);
m_eveRecv+=text;
time+=setting.m_tp;
SetTimer(WM_SENDACK,setting.m_tp,NULL); // 发送ACK
}
}
UpdateData(false);
}
void CARQDlg::SendACK()
{
CString text;
text.Format("发送ACK\r\n");
m_eveRecv+=text;
time+=setting.m_ts;
int p=rand();
if(p%100>setting.m_missing)
{
time+=setting.m_tl;
SetTimer(WM_ONACK,setting.m_tl,NULL);
}
else
{
text.Format("ACK不幸丢失 \r\n");
m_eveRecv+=text;
}
UpdateData(false);
}
void CARQDlg::SendNAK()
{
CString text;
text.Format("发送NAK\r\n");
m_eveRecv+=text;
int p=rand();
if(p%100>setting.m_missing)
{
time+=setting.m_tl;
SetTimer(WM_ONNAK,setting.m_tl,NULL);
}
else
{
text.Format("NAK不幸丢失 \r\n");
m_eveRecv+=text;
}
UpdateData(false);
}
13.编译调试,输入20,可能会得到下图结果,(根据随机数不同而不同
)
五、 实验总结
1. 通过4次试验,加上平时的练习,实验达到实验目的;
2. 通过实现协议,加深了对协议的理解;
3. 对c++6.0的功能有了进一步的认识,感觉挺好用的;
4. 当然我没学过c++,实验代码都是老师给的,函数名字都不敢改,我就严格用老师提供的ID名。