CSMA/CD算法C语言编程模拟

CSMA/CD工作过程介绍

  1. 准备发送:适配器从网络层获得一个分组,加上以太网的首部和尾部,组成以太网帧,组成以太网帧,放入适配器的缓存中。但在发送之前,必须先检测信道。
    在这里插入图片描述
  2. 检测信道:若检测到信道忙,则应不停地检测,一直等待信道为空闲。若检测到信道空闲,并在96bit时间内信道保持空闲(保证了帧间最小间隔),就发送这个帧
  3. 在发送过程中仍不停地检测信道,即网络适配器要边发边监听。这里只有两种可能性:
    i:发送成功:在争用期内一直未检测到碰撞。这个帧肯定能够发送成功。发送完毕后,其他什么也不做。然后回到步骤1.
    ii:发送失败:在争用期内检测到碰撞。这时立即停止发送数据,并按规定发送人为干扰信号32bit或48bit(我在下面模拟过程中,使用的48bit)。适配器接着就执行指数退避算法,等待r倍512bit时间后,返回到步骤2.继续检测信道。但若重传次数达到一定次数仍不能成功(下面算法过程中,我设置为了10次),则停止重传而向上报错。
    注:以太网每发完一帧,一定要把已发送的帧暂时保留一下。如果在争用期内检测出发生碰撞,那么还要在推迟一段时间后再把这个暂时保留的帧重传

CSMA/CD过程流程图

在这里插入图片描述

CSMA/CD算法C语言编程模拟代码

#include<stdio.h>
#include<string.h>
#include<pthread.h>
#include<windows.h>
#include<time.h>
#include<stdlib.h>
#include<math.h>


#define ABitSleepTime 100  
#define RoadLength   100                //信道长度,100bit 
#define ContentionPeriod  512          //争用期为512bit和最短帧长512bit 
#define EnhanceConflictSignals 48      //增强冲突信号48bit 

char road[RoadLength];                 //定义信道 


/* 模拟传输一个位需要的时间定义 */ 
void BitTime(int bit)
{
	while(bit-- != 0)
	{
		Sleep(ABitSleepTime);
	}	
}

/* 模拟信道监听96bit  */ 
void ChannelMonitor(int tranStart)
{ 
	int idleBit = 96;
	while(idleBit-- != 0)
	{
		printf("tranStart:%d在信道监听 idleBit:%d\n", tranStart, idleBit);
		if(road[tranStart] != '-')
		{
			/* 使用字符'-'代表信道空闲,若信道不空闲,信道road的字符串会给修改其它值,继续监听96,直到信道一直空闲96bit为止 */
			idleBit = 96;
		}
		BitTime(1);
	}
} 


/* 模拟指数回退 */
void IndexReg(int retranCount, int tranStart)
{ 

	int r = 0;
	srand(time(NULL) - tranStart * 100000);
	
	/* 使用随机数获取回退的次数 */
	r = rand()%4; 

	printf("tranStart:%d在指数回退 指数回退次数:%d\n", tranStart, r);
	BitTime(r * ContentionPeriod); //
}

void* Client(void *arg)
{
	 /* 通信主机 *arg为1或者-1 data为传输最小帧长 + 封装需要的8字节 8 */
	int *flag = (int *)arg, tranStart = -1, data = ContentionPeriod + 8 * 8, j = 0, retranCount = 0; //
	
	/* 确定是哪一个主机,确定信道传输的方向 ,是从左往右传数据,还是从右往左传数据 */
	if(*flag == 1) 
	{
		tranStart = 0; 
	}
	else
	{
		tranStart = RoadLength - 1;	
	} 

	while(1)
	{
		/* 监听到信道空闲持续96bit后,准备进行边监听边传输 */ 
		ChannelMonitor(tranStart);

		while((data--) != 0)
		{
			printf("A==>  %s   <==B\n", road);
			
			/* 判断信道是否存在冲突,用字符'-'代表信道空闲,若信道不空闲,信道road的字符串会给修改其它值 */
			if(road[tranStart + j] == '-')
			{
				/* 判断数据是否已经传送到另一端的主机端,若没有,则开始将数据进行传送 */ 
				if(abs(j) == RoadLength - 1)
				{ 
					road[tranStart + j] = 'B' - *flag;
					BitTime(1);
					printf("A==>  %s   <==B\n", road);
					if(data != 0)
					{
						 /* 因为没有检测到冲突,则可以认为后面传输不会出现冲突 */
						BitTime(data);
						data = 0; 
						j = 0; 
						while(abs(j) != RoadLength - 1)
						{
							//信号逐渐传向另一边 
							road[tranStart + j] = '-' ;
							j += (*flag);
							BitTime(1);
						}
						printf("tranStart:%d传送成功,需要重传的次数%d\n", tranStart, retranCount);
						memset(road, '-', sizeof(road)); //辅助信道一定清空 
						retranCount = 0;
					}
				}
				else
				{
					road[tranStart + j] = 'B' - *flag;
					
					/* 利用创建两个线程时分别传入的1和-1进行加减,便于计算出两边传输数据时的下标,当线程使用的是1,代表数据从左边开始传输,当线程使用参数-1,则代表数据从右边开始传输 */ 
					j += (*flag);
				}
				
			}
			else
			{
				//发生冲突,发送48bit的增强冲突,然后进入随机指数回退算法
				printf("tranStart:%d发生冲突\n", tranStart);
				retranCount += 1;
				printf("tranStart:%d发送人为增强冲突信号%d\n", tranStart, EnhanceConflictSignals);
				BitTime(EnhanceConflictSignals + RoadLength);
				memset(road, '-', sizeof(road));//清空信道,进入指数回退算法
				printf("tranStart:%d发送人为增强冲突信号完成\n", tranStart);
				if(retranCount > 10)
				{
					printf("重传次数超过10次,不再继续重传\n");
					retranCount = 0;
					break;
				}
				else
				{//进入指数回退算法
					printf("tranStart:%d进入回退算法\n", tranStart);
					IndexReg(retranCount, tranStart);
					break;
				}
			}
			BitTime(1);		
		}
		data = ContentionPeriod + 8 * 8;
		j = 0;
	}
	  
} 


int main(void)
{
	int tranStart1 = 1, tranStart2 = -1;
	pthread_t thid1,thid2;
	memset(road, '-', sizeof(road));

	/* 创建线程1,使用tranStart1 == 1参数代表是线程2 */
	printf("创建主机A\n")
	if(pthread_create(&thid1, NULL, Client, &tranStart1))
	{
		printf("thread1 creation failed\n");
		exit(1);
	}
	
	/* 创建线程2,使用tranStart2 == -1参数代表是线程2 */
	printf("创建主机B\n");
	if(pthread_create(&thid2, NULL, Client, &tranStart2))
	{
		printf("thread2 creation failed\n");
		exit(1);
	}
	while(1)
	{
		BitTime(10);
	}
	return 0;
} 




注:

对于部分说包含#include<pthread.h>无法编译的问题,这是因为原来由于pthread库不是Linux系统默认的库,连接时需要使用库libpthread.a,可以参考以下文章解决:

https://blog.csdn.net/m0_37251750/article/details/126406672

https://blog.csdn.net/freiheit_zz/article/details/118030587

  • 16
    点赞
  • 78
    收藏
    觉得还不错? 一键收藏
  • 26
    评论
对于无时隙的CSMA/CA算法,可以使用以下C语言代码实现: ```c #include <stdio.h> #include <stdlib.h> #include <time.h> #define SLOT_TIME 1 //时隙时间 #define CW_MIN 4 //最小退避窗口大小 #define CW_MAX 1024 //最大退避窗口大小 #define MAX_RETRIES 10 //最大重试次数 #define SUCCESS_PROB 0.8 //发送成功的概率 int main(void) { //初始化随机数种子 srand(time(NULL)); int backoff = CW_MIN; //退避窗口大小 int collisions = 0; //碰撞次数 int retries = 0; //重试次数 while (retries < MAX_RETRIES) { //等待一个随机的时间长度 int wait_time = rand() % backoff * SLOT_TIME; printf("等待 %d 个时隙...\n", wait_time); for (int i = 0; i < wait_time; i++) { //模拟等待 ; } //尝试发送 if (rand() < RAND_MAX * SUCCESS_PROB) { printf("发送成功!\n"); break; } else { printf("发送失败!\n"); collisions++; if (collisions >= 10) { //发生了太多次碰撞,增加退避窗口大小 backoff = backoff * 2; if (backoff > CW_MAX) { backoff = CW_MAX; } collisions = 0; } else { //增加退避窗口大小 backoff = backoff * 2; if (backoff > CW_MAX) { backoff = CW_MAX; } } retries++; } } if (retries >= MAX_RETRIES) { printf("发送失败!\n"); } return 0; } ``` 代码使用了随机数来模拟退避过程,当发送成功时退出循环,否则增加退避窗口大小并重试。当发生太多次碰撞时,增加退避窗口的大小以减少碰撞的次数。如果重试次数达到了最大限制,发送失败。
评论 26
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值