CSMA/CD
CSMA/CD(Carrier Sense Multiple Access with Collision Detection)即带冲突检测的载波监听多路访问技术(载波监听多点接入/碰撞检测)。
CSMA/CD协议的要点:
- “多点接入”:就是说明这是一种对点接入协议,许多计算机以多点接入的方式连接在一根总线上。协议的实质是“载波监听”和“碰撞检测”;
- “载波监听”(发送前先监听):检测信道;不管在发送前,还是在发送中,每个站都必须不停地检测信道。
- 在发送前检测信道,是为了获得发送权,如果检测出已经有其他站在发送,则自己就暂时不许发送数据,必须等到信道变为空闲时才能发送;
- 在发送中检测信道,是为了及时发现有没有其他站的发送和本站发送的碰撞;
- “碰撞检测”(边发送边监听):
- 即适配器边发送数据边检测信道上的信号电压的变化情况,以便判断自己在发送数据时其他站是否也在发送数据;
- 当几个站同时在总线上发送数据时,总线上的信号电压变化幅度将会增大(相互叠加),当适配器检测到的信号电压变化幅度超过一定的门限值时,就认为总线上至少有两个站同时在发送数据,表明产生了碰撞;
流程图
截断二进制指数退避算法
截断二进制指数退避(truncated binary exponential backoff)算法是用来解决以太网碰撞问题的一种算法。这种算法让发生碰撞的的站在停止发送数据后,不是等待信道变为空闲后就立即在发送数据,而是推迟(这叫做退避)一个随机的时间。这样做是为了重传是再次发生冲突的概率减小。(百度百科)
算法过程
-
重传应推后r倍的争用期
争用期就是以太网端到端的往返时间2t,即512比特。以太网把争用期定为51.2μs。对于10Mb/s以太网,在争用期内可发送512bit,即64字节。也可以说争用期是512比特时间。1比特时间就是发送1比特所需要的时间。所以这种时间单位与数据率密切相关。 -
从离散的整数集合[0,1,…,]中随机取出一个数,记为r。重传应推后的时间就是r倍的争用期。上面的参数k按下面的公式计算:
k=Min[重传次数,10]
可见,当重传次数≤10时,参数k=重传次数;
但当重传次数>10时,k=10。 -
当重传达16次仍不能成功时(这表明同时打算发送的数据站太多,以致连续发生冲突),则丢弃该,并向高层报告。
例如,在第1次重传时,k=1,随机数r从整数{0,1}中选一个数。因此重传推迟的时间是0或争用期,在这两个时间中随机选择一个。
若再发生碰撞,则重传时,k=2,随机数r就从整数{0,1,2,3}中选一个数。因此重传推迟的时间是在0,2 ,4 和6 这4个时间中随机抽取一个。
同样,若在发生碰撞,则重传时k=3,随机数r就从整数{0,1,2,3,4,5,6,7}中选一个数。以此类推。
若连续多次发生冲突,就表明可能有较多的站参与争用信道。但使用退避算法可使重传需要推迟的平均时间随重传次数而增大(这也称为动态退避),因而减小发生碰撞的概率,有利于整个系统的稳定。
代码模拟(Java)
package demo;
import java.io.IOException;
import java.util.Random;
import java.util.Scanner;
public class TruncatedBinaryExponentialEscapeAlgorithm {
static double T=51.2;//争用期时间
public static void main(String[] args) throws IOException {
int k=0;//重传次数
double p=0;//百分比的碰撞概率
System.out.print("关于CSMA/CD协议截断二进制退避指数算法演示demo\n请输入自定义的碰撞发生的概率:");
Scanner sc = new Scanner(System.in);
p=sc.nextDouble();
System.out.println(p);
while(true){//不断地进行重传
if(getcrash(p)){//发生碰撞
k++;
if (k <= 16)
{
System.out.println("发生碰撞,准备进行第" + k + "次重传!");
System.out.println("退避时间为:"+backtime(k)+"μs");
System.out.println("正在退避中........");
System.out.println("退避完成,开始重传!");
continue;
}
if (k > 16) {
System.out.println("第16次重传失败,丢弃本帧,向高层报告!");
break;
}
}else{//没有发生碰撞
System.out.println("此次没有发生碰撞,传送成功,一共重传" + k + "次!");
break;
}
}
}
//判断是否发生碰撞
static boolean getcrash(double p){
Random random = new Random();
double r=random.nextInt(100)+1;
if(r<=p){
return true;
}else{
return false;
}
}
// 计算r的范围中2的k次方
static int calculate(int k) {
if (k == 0)
return 1;
return 2 * calculate(k - 1);
}
//计算退避时间
static double backtime(int k) {
int r = 0 , power = 0;
double time;
if (k <= 10)
power = calculate(k) - 1;
if (k > 10)
{
power = calculate(10) - 1;
}
Random random = new Random();
r=random.nextInt(power+1);
time = r * T;
return time;
}
}