nRetCode=1;
}
else
{
thread1=AfxBeginThread(aThread,NULL);//启动线程a
ID1=thread1->m_nThreadID;//获取线程ID号
thread2=AfxBeginThread(bThread,NULL);//启动线程b
ID2=thread2->m_nThreadID;//获取线程ID号
getchar();
}
returnnRetCode;
}
UINTaThread(LPVOIDpParam)//线程a(主机a)
{
inti=0;//发送成功次数
intCollisionCounter=0;//冲突计数器初始值为16
doubleCollisionwindow=0.05;//冲突窗口值取0.05
intrandNum=rand()%3;//随机数可用Srand函数改变随机函数的种子,改善随机性
Loop:
if(Bus==0)//总线空闲
{
Bus=Bus|ID1;//模拟发包
Sleep(12);//单位是毫秒
if(Bus==ID1)//无冲突,由发送方负责检测
{
printf("%dSendSuccess\n\n",ID1);//发送成功
Bus=0;//内存清零
CollisionCounter=0;//复原冲如计数器
Sleep(rand()%10);//
i++;
printf("主机a发送成功次数=%d\n\n",i);
if(i<10)
gotoLoop;//发送次数不够10次,开始下一次发送
}
else
{
printf("%dSendCollision\n\n",ID1);//发生冲突
Bus=0;
CollisionCounter++;//冲突计数器减1
if(CollisionCounter<16)
{
Sleep(randNum*(int)pow(2.0,(CollisionCounter>10)?
10:
CollisionCounter)*Collisionwindow);//
gotoLoop;//下一次尝试发送
}
else
printf("%ldSendFailure\n\n",ID1);//重发次数超过16次,宣布发送失败
}
}
else//总线忙
gotoLoop;//继续载波侦听
return0;
}
UINTbThread(LPVOIDpParam)//线程b
{
intj=0;//发送成功次数
intCollisionCounter=0;//冲突计数器初始值为16
doubleCollisionWindow=0.05;//为争用期(51.2us),以时间为单位的冲突窗口
intrandNum=rand()%3;//随机数
Loop:
if(Bus==0)//总线空闲
{
Sleep
(2);//可用随机函数模拟其他用户随机接入//④
Bus=Bus|ID2;//模拟发包
Sleep(3);//⑤
if(Bus==ID2)//无冲突
{
printf("%dSendSuccess\n\n",ID2);//发送成功
Bus=0;//总线清零
CollisionCounter=0;//复原冲突计数器
Sleep(rand()%10);
j++;
printf("主机b发送成功次数=%d\n\n",j);
if(j!
=10)//不够10次开始下一次发送
gotoLoop;
}
else
{
printf("%dSendCollision\n\n",ID2);
Bus=0;
CollisionCounter++;//冲突计数器减1
if(CollisionCounter<16)//随机延迟重发,延迟算法用截止二进制指数后退算法
{Sleep(randNum*(int)pow(2.0,(CollisionCounter>10)?
10:
CollisionCounter)*CollisionWindow);
gotoLoop;
}
else
{printf("%dSendFailure\n\n",ID2);}
}
}
else//总线忙
gotoLoop;//继续装载波侦听
return0;
}
注:
程序在VC下编制、执行。
五、仿真说明及程序分析
1.设计中的重点及难点
1)模拟冲突的过程,在这个程序中不要使用任何线程同步机制,以保证各线程执行的随机性;
2)若程序中不能模拟出冲突,可以在某些地方加入延时;
3)程序产生冲突主要取决于各线程能否交叉执行,具体又取决于CPU数、每一线程需要运行的时间等;
2.程序流程
图2为主程序流程图。
图3为主程序中的线程流程图,其中线程A和线程B的线程相同。
图2主程序流程图
图3线程流程图
六、sleep函数分析
对照第四项中参考仿真程序,按sleep使用顺序先后依次讨论:
1 Sleep(12):
线程a发送时延为12ms,改变sleep函数参数来观察不同时延对碰撞的影响,有:
时延(ms)
运行5次,每次出现冲突的次数
平均值
2
3
6
6
3
6
4.8
12
4
2
2
2
2
2.4
120
3
3
3
3
3
3
1200
2
2
2
2
2
2
理论上分析传播及发送时延越大,冲突越大,但实际运行结果不能反映。
随着时延参数数量级的增加,一次程序运行的时间也明显增加,符合实际。
但当时延达到几十秒时,显然不利于数据的高效传输。
2 Sleep(rand()%10):
确认一数据帧发送成功后随机等待一个不超过10ms的时间。
此处是模拟的帧间隙,即发送成功后等待一帧间隙继续准备发送下一帧。
考虑到CSMA/CD协议的帧间隙固定为9.6us,在改进后的程序(见附录)中此处改作Sleep(0.0096)。
3 Sleep(randNum*(int)pow(2.0,(CollisionCounter>10)?
10:
CollisionCounter)*Collisionwindow):
检测到冲突、停止发送后,结点进行随机延迟后重发。
随机延迟采用截止二进制指数后退算法。
该算法可表示为:
T=2*R*a。
其中T为结点重新发送需要的后退延迟时间,Collisionwindow为冲突窗口值a,randNum为随机数R,从0到2k-1中取值,k的取值为min(n,10),CollisionCounter为该帧已被发送的次数n。
4 Sleep
(2):
为避免a,b线程sleep设置完全一致(此时仿真结果为a,b交替发送,无冲突),b检测到总线空闲时,延时2ms进行发送。
可用随机函数模拟其他用户随机接入,即改为:
Sleep(rand()%x),减少冲突的几率。
考虑到争用期为51.2ms,随机等待的时间不能超过这一上限值。
结果汇总如下:
随机等待的时间x(ms)
0~9
0~50
冲突次数均值(次)
10
4
可见,随机接入确实有助于减少冲突的几率。
5 Sleep(3):
模拟从发包到监测到信道忙之间的时间,按照CSMA/CD协议,争用期和检测到信道忙有着充要关系,于是在改进的程序中此处与④的随机时间加起来和是一个时间51.2us。
七、协议的改进
1 增加窗口值
将0.05改为0.10,窗口值增大,冲突次数减少,所需时间也有所下降。
2 增加用户数
初始设置增加:
*thread3以及UINTcThread(LPVOIDpParam);应用进程增加:
UINTcThread(LPVOIDpParam)//线程c(同线程b)。
图4是运行后的结果截图。
3 改变发送策略
参考程序中的发送策略为“1坚持的CSMA”:
线路忙,继续侦听;不忙时,立即发送;能提高信道利用率,但带来更大可能的冲突。
改进后的程序采用“p坚持的CSMA”:
线路忙,继续侦听;不忙时,根据p概率进行发送。
多次运行发现,冲突的平均次数明显下降。
4 改变侦听策略
参考程序中线路忙,继续侦听;改进后的程序采用线路忙,等待一段时间,再侦听的侦听策略。
图4增加一个线程后运行结果
八、心得体会