模拟总线型以太网数据帧发送过程

2、要求

(1)在一台计算机上模拟总线型以太网数据帧发送过程,总线上连接的计算机个数为2个,支持CSMA\CD协议(二进制指数退避算法)

(2)用两个线程a和b模拟以太网上的两台主机。用一个双字类型变量Bus模拟总线。

(3)两个子线程向总线发送自己的数据。数据用该线程的线程号进行模拟,发送数据用线程号的Bus的“或”进行模拟。每台主机须向总线上成功发送5次数据,如果其中某次数据发送失败,则该线程结束。

  1. 相关知识

以太网的核心技术是随机争用型介质访问方法,即带有冲突检测的载波侦听多路访问(CSMA/CD)方法。

1.以太网的帧的发送流程

(1)载波侦听过程。以太网中每个节点利用总线发送数据,总线是每个结点共享的公共传输介质。所以结点在发送一个帧前,必须侦听总线是否空闲,由于以太网的数据采用曼彻斯特编码方式,所以可以通过判断总线电平是否跳变来确定总线是否空闲。若总线空闲,就可启动发送,否则继续侦听。

(2)冲突检测。在数据发送过程中,可能会产生冲突(冲突是指总线上同时出现两个或两个以上的发送信号,他们叠加后的信号波形与任何发送结点输出的信号波形不相同。因为可能有多个主机都在侦听总线,当它们侦听到总线空闲时,都会往总线上发送数据)。所以在发送数据的过程中,也应该进行冲突检测,只要发现冲突就应该停止发送数据。

(2)随机延迟后重发。在检测到冲突、停止发送后,结点进行随机延迟重发。若重发16次后还没成功,则宣告发送失败,取消该帧的发送。随机延迟的计算方法一般采用截至二进制指数后退算法。该算法可表示为:t=2k*R*a ,其中t为结点重新发送需要的后退延迟时间,a为冲突窗口值(冲突窗口为总线最大长度和电磁波在介质中的传播速度比值的2倍),R为随机数,k的取值为k=min(n,10),n为该帧已发送的次数。

  1. 实现原理

原理:

发送方:

某站点需要发送数据帧,首先侦听信道:

  1. 如果信道空闲,站点立即发送数据帧;
  2. 在发送数据帧过程中,边发送边冲突检测;
  3. 如果信道忙,继续侦听直到信道变为空闲,再发送数据帧

如果再发送过程中检测到冲突,则:

  1. 立即停止发送该数据帧;
  2. 给总线上发送一串阻塞加强信号,告诉其他站点总线发生冲突;
  3. 等待一段随机时间(利用二进制指数退避算法),再重新争用总线,重复上面步骤,并重发该数据帧。

接收方:

  1. 检查是否发生冲突,若发生冲突,则丢弃该帧;若没有冲突,进入下一步。
  2. 检查该帧的目的地址是否可以接收该帧,若可以接收,则进入下一步。
  3. 检查CRC校验和LLC数据长度。若都正确,接收该帧,否则丢弃。

5、程序代码

#include <iostream>
#include <thread>
#include <stdlib.h>
#include <windows.h>
#include <math.h>
#include <mutex>
#define T 5 //在这里,以毫秒为单位,故延迟窗口为5ms
using namespace std;
int BUS;
int randnum[128];//随机数库
int randnumi;//随机数库访问变量
mutex myout;
class Host
{
public:
    char id;              //主机ID
    int centcount;        //发送次数
    int collisionCounter; //发送失败最对次数
    int successcount;     //成功计数器
    int data;
    Host(char idIN, int a, int b, int sc)
    {
        id = idIN;
        centcount = a;
        collisionCounter = b;
        successcount = sc;
        data = randnum[randnumi++];
        myout.lock();
        cout << "主机" << id << "已就绪." << endl;
        myout.unlock();
    }
    int csmacd(int a,int randn)
    {
        int k = min(a, 10);
        int r = randn % k;
        return (pow(2, r) - 1) * T;
    }

    void send()
    {
        int cCounter = 0; //失败次数计数器
        while (true)
        {
            if (successcount >= centcount)
                break;
            if (BUS == 0)
            {
                Sleep((int)(T*(randnum[randnumi++]%5) )); //模拟占用总线的时间.
                BUS = BUS | data;
                Sleep((int)(T*(randnum[randnumi++]%5) ));
                if (BUS == data)
                {
                    successcount++;
                    myout.lock();
                    cout << id << " send " << data << " success,总 " << successcount << " 次成功. ///" << endl;
                    myout.unlock();
                    cCounter = 0;
                    data=randnum[randnumi++];
                    BUS = 0;
                }
                else
                {
                    cCounter++;
                    myout.lock();
                    cout << id << " send " << data << " collision,第" <<cCounter<<" 次失败."<< endl;
                    myout.unlock();
                    BUS = 0;
                    Sleep(csmacd(cCounter,randnum[randnumi++]));

                    if (cCounter >= collisionCounter)
                    {
                        myout.lock();
                        cout << id << " send" << data << " failure" << endl;
                        myout.unlock();
                        data=randnum[randnumi];
                        cCounter = 0;
                        BUS = 0;
                    }
                }
            }
            else
            {
                Sleep(randnum[randnumi++] % 10);
            }
        }
    }
};
void a()
{
    Host host('A', 5, 16, 0); //第二个参数为修改的需要成功的次数
    host.send();
}
void b()
{
    Host host('B', 5, 16, 0);
    host.send();
}
int main()
{   srand(time(0));
    for(int i=0;i<128;i++){
        randnum[i]=rand();
    }
    randnumi=0;
    thread A(a);
    thread B(b);
    A.join();
    B.join();
    return 0;
}

6、运行结果与分析

 

       经过多次测试, 可以观察到,数据的冲突具有随机性, 并且由于在一开始两个发送方都征用信道,造成大概率的信道堵塞, 多次发送失败, 当发送失败次数多了后, 就有一方有较大的随即延迟, 此时另一方即可迅速发完. 最后当两个节点发送数据基本不再冲突.

       我的报告中对发送方式的修改: 修改为在一个线程中连续发送五次成功后才退出线程. (基本原理与创建线程发送无异).

  • 3
    点赞
  • 42
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
以太网是数据在以太网中传输时的一种格式,它包含了目标MAC地址、源MAC地址、协议类型、数据等内容。下面是基于Java Socket模拟以太网的封装和发送过程的示例代码: 1. 创建Socket对象,指定目标IP地址和端口号 ```java Socket socket = new Socket("目标IP地址", 端口号); ``` 2. 获取Socket对象的输出流 ```java OutputStream outputStream = socket.getOutputStream(); ``` 3. 构建以太网数据包,将数据写入输出流中 ```java byte[] frameData = new byte[1024]; //设置目标MAC地址 frameData[0] = (byte)0xff; frameData[1] = (byte)0xff; frameData[2] = (byte)0xff; frameData[3] = (byte)0xff; frameData[4] = (byte)0xff; frameData[5] = (byte)0xff; //设置源MAC地址 frameData[6] = (byte)0x11; frameData[7] = (byte)0x22; frameData[8] = (byte)0x33; frameData[9] = (byte)0x44; frameData[10] = (byte)0x55; frameData[11] = (byte)0x66; //设置协议类型为IP数据包 frameData[12] = (byte)0x08; frameData[13] = (byte)0x00; //设置数据 String message = "Hello, World!"; byte[] messageBytes = message.getBytes(); System.arraycopy(messageBytes, 0, frameData, 14, messageBytes.length); outputStream.write(frameData, 0, 14 + messageBytes.length); outputStream.flush(); ``` 4. 关闭Socket连接 ```java socket.close(); ``` 在这个示例代码中,我们通过Socket的输出流写入以太网数据包。首先设置了目标MAC地址、源MAC地址和协议类型,然后将数据写入到数据区域中。最后将数据包写入到输出流中,发送给目标IP地址。注意在实际的应用中,需要根据具体的协议规范来构建以太网数据包。
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值