}
运行结果:
运行结果如下所示:
执行EncapFrame.exe文件的结果如下所示:
模拟Ethernet帧的发送过程
题目:
编写程序模拟Ethernet结点的数据发送流程
设计要求:
(1)用两个线程a,b来模拟Ethernet上的两台主机。
(2)用一个双字类型变量Bus来模拟总线(将其初始化为“/0”,并且总线等于“/0”时表示总线空闲)。
(3)两个子线程向总线发送自己的数据。
数据用该线程的线程信号进行模拟,发送数据用线程号和Bus的“或”操作进行模拟(即Bus=Bus|ID,ID为该线程的线程号)。
(4)每台主机须向总线成功发送10次数据,如果其中某次数据发送失败,则该线程结束。
(5)发送流程须遵循CSMA/CD。
随机延迟算法中的冲突窗口取0.005。
在数据发送成功(即Bus==ID)后,报告“IDsendsuccess”,产生冲突(即Bus!
=ID)后,报告“IDsendcollision”,发送失败(即冲突计数器值为0)后报告“IDsendfailure”。
随着主机发送成功次数的增加,报告其已发送成功的次数,如“主机A发送成功次数=3”。
三、可行性分析
1.技术可行性
技术可行性分析包括:
风险分析、资源分析和技术分析。
风险分析的任务是,在给定的约束条件下,判断能否设计并实现系统所需功能和性能。
资源分析的任务是,论证是否具有系统开发所需的各类人员(管理人员和各类专业技术人员)、软件、硬件资源和工作环境等。
技术分析的任务是,论证当前的科学技术是否支持系统开发的全过程。
其中最主要的是分析技术条件是否能顺利完成开发工作,硬、软件能否满足开发者的需要等。
2.功能分析
基本功能
1.在一台计算机上实现,用多个程序或线程来模拟多个计算机
2.使用一个共享数据区来模拟总线
3.模拟实现载波监听的过程
4.模拟实现发生冲突的过程和冲突的处理机制
四.Ethernet帧的发送与接收流程
1Ethernet帧的发送流程
1)载波侦听过程。
Ethernet中每个结点利用总线发送数据,总线是每个结点共享的公共传输介质。
所以结点在发送一个帧前,必须侦听总线是否空闲。
由于Ethernet的数据采用曼彻斯特编码方式,所以可以通过判断总线电平是否跳变来确定总线是否空闲。
若总线空闲,就可以启动发送,否则继续侦听。
2)冲突检测。
在数据发送过程中,可能会产生冲突(冲突是指总线上同时出现两个或两个以上的发送信号,他们叠加后的信号波形与任何发送结点的输出波形都不相同)。
因为可能有多个主机都在侦听总线,当它们侦听到总线空闲时,就会往总线上发送数据。
所以在发送数据的过程中,也应该进行冲突检测,只要发现冲突就应该立即停止发送数据。
3)随即延迟后重发。
在检测到冲突、停止发送后,结点进行随机延迟后重发。
若发16次后还没成功,则宣告发送失败,取消该帧的发送。
随机延迟的算法一般采用截断的二进制指数退避算法。
当出现线路冲突时,如果冲突的各站点都采用同样的退避间隔时间,则很容易产生二次、三次的碰撞。
因此,要求各个站点的退避间隔时间具有差异性。
这要求通过退避算法来实现。
当一个站点发现线路忙时,要等待一个延时时间M,然后再进行侦听工作。
延时时间M由以下算法决定:
M=2k*R*a。
其中a为冲突窗口值(冲突窗口为总线最大长度和电磁波在介质中传播速度比值的2倍),R为随机数,k的取值为k=min(n,16),n为该帧已被发送的次数。
图1给出了Ethernet帧的发送流程。
2Ethernet帧的接收流程
帧的接收流程大致可以分为以下三个步骤:
1)检查是否发生冲突,若发生冲突,则丢弃该帧;若没有冲突,进入下一步。
2)检查该帧的目的地址看是否可以接收该帧,若可以接收,则进入下一步。
3)检查CRC检验和LLC数据长度。
若都正确,接受该帧,否则丢弃。
图1Ethernet帧的发送流程
图2主程序流程图
五.详细设计
1.设计中的重点及难点
1)模拟冲突过程,在这个程序中不要使用任何线程同步机制。
2)若程序中不能模拟出冲突,可以在某些地方加入延时。
2.核心代码
#include
#include
#include
HANDLEhTread1,hTread2;//线程句柄
DWORDdwThreadId1,dwThreadId2;//线程ID号
DWORDBus=0;//总线
DWORDWINAPIThreadProcA(LPVOIDlpParam)
{
inti=0;//发送成功次数
intCollisionCounter=11;//冲突计数器初始值为11
doublecollisionWindow=0.005;//冲突窗口值取0.005
intrandNum=rand()%3;//随机数
Loop:
if(Bus==0)//总线空闲
{
Bus=Bus|dwThreadId1;//模拟发送包
Sleep(12);
if(Bus==dwThreadId1)//数据发送成功
{
printf("%dSendSuccess\n\n",dwThreadId1);//发送成功
Bus=0;//内存清零
CollisionCounter=11;//复原冲突计数器
Sleep(rand()%10);//随机延时
i++;
printf("主机a发送成功次数=%d\n\n",i);
if(i<10)
gotoLoop;//发送次数不够10次,开始下一次发送
}
else//数据发送失败
{
printf("%dSendCollision\n\n",dwThreadId1);//发生冲突
Bus=0;
CollisionCounter--;//冲突计数器减1
if(CollisionCounter>0)
{//随机延迟重发,延迟算法用截止二进制指数后退算法
Sleep((unsignedlong)(randNum*(int)pow(2,(CollisionCounter>10)?
10:
CollisionCounter)*collisionWindow));
gotoLoop;
}
else
{
printf("%ldSendFailure\n\n",dwThreadId1);//重发次数超过16次,宣布发送失败
}
}
}
else//总线忙
gotoLoop;//继续载波侦听
return0;
}
DWORDWINAPIThreadProcB(LPVOIDlpParam)//返回DWORD(32位数据)的API函数
{
intj=0;
intCollisionCounter=11;
doublecollisionWindow=0.005;
intrandNum=rand()%3;
Loop:
if(Bus==0)
{
Sleep
(2);
Bus=Bus|dwThreadId2;
Sleep(3);
if(Bus==dwThreadId2)//数据发送成功
{
printf("%dSendSuccess\n\n",dwThreadId2);
Bus=0;
CollisionCounter=11;
Sleep(rand()%10);
j++;
printf("主机b发送成功次数=%d\n\n",j);
if(j<10)
gotoLoop;
}
else//数据发送失败
{
printf("%dSendCollision\n\n",dwThreadId2);
Bus=0;
CollisionCounter--;
if(CollisionCounter>0)
{
Sleep((unsignedlong)(randNum*(int)pow(2,(CollisionCounter>10)?
10:
CollisionCounter)*collisionWindow));
gotoLoop;
}
else
{
printf("%ldSendFailure\n\n",dwThreadId2);
}
}
}
else
gotoLoop;
return0;
}
intmain(intargc,char*argv[])
{
hTread1=:
:
CreateThread(NULL,NULL,ThreadProcA,NULL,0,&dwThreadId1);
//WaitForSingleObject(hTread2,INFINITE);
hTread2=:
:
CreateThread(NULL,NULL,ThreadProcB,NULL,0,&dwThreadId2);
WaitForSingleObject(hTread2,INFINITE);
:
:
CloseHandle(hTread1);
:
:
CloseHandle(hTread2);
return0;
}
七.运行结果