通过控制台输入一段文字,输出MAC帧的2进制和16进制的字符串,主要是求FCS。这里只考虑单帧的情况,即只考虑输入数据在1字节~1500字节之间的情况,对于更长的数据暂不考虑。

1、MAC帧基本格式

表1 802.3标准的帧结构

前导码

帧前定界符

目的地址

源地址

长度字段

数据字段

校验字段

7B

1B

(6B)

(6B)

(2B)

46~1500字节

(4B)

具体内容参考:https://blog.csdn.net/icxiaoge/article/details/83420269

2、内容

目的地址:8000FF602CDC

源地址:8000FE853A5F

生成多项式:

3、程序思路

  1. 目的地址和源地址是已知的,由于计算CRC检验是2进制形式,可写函数hexToBinary(),将目的地址和源地址转换成2进制

  2. 右生成多项式得到用于CRC检验的除数p为:100000100110000010001110110110111

  3. 数据字段用ASCII编码,写函数dataToBinaryString()将数据转换成2进制字符串

  4. 长度字段由首部尾部18字节与数据字段长度(46字节~1500字节)和的2进制串

  5. 将目的地址、源地址、长度字段、数据字段组成MAC帧的前部分

  6. 利用得到的MAC帧前部分,用函数solveFcs()求出FCS

4、代码

主类MacFrameEncapsulation.java如下:

/*
* 文件内容:MAC帧封装
* 文件目标:输入一段不超过1500个字节的数据,输出封装后的MAC帧,
* MAC帧包含目的地址、源地址、长度/类型、数据,FCS 共5部分
* 各部分有单独输出显示,输出为2进制和16进制
* 时间:2018-11-15
* 作者:wowpH
*/ import java.util.Scanner; public class MacFrameEncapsulation { private int LENGTH_FIELD_BYTES = 2; // 长度字段长度为2 bytes
private int MIN_DATA_BYTES_LENGTH = 46; // 数据字段最小长度46 bytes
private int MIN_DATA_BITS_LENGTH = 368; // 数据字段最小长度368 bits
private int MAX_DATA_BYTES_LENGTH = 1500; // 数据字段最大长度1500 bytes
private int FCS_BITS_LENGTH = 32; // FCS长度(bit)
private String DESTINATION_ADDRESS = "8000FF602CDC"; // 目的地址
private String SOURCE_ADDRESS = "8000FE853A5F"; // 源地址
private String DIVISOR_P = "100000100110000010001110110110111"; // CRC检验的除数p
private String destinationBinaryAddress = ""; // 目的地址二进制
private String sourceBinaryAddress = ""; // 源地址二进制
private String lengthField = ""; // 长度字段2进制,2 bytes
private String data = ""; // 输入的数据部分
private String dataBinary = ""; // 输入的数据部分的2进制
private String fcs = ""; // CRC检验的FCS二进制
private String mac = ""; // MAC帧,包含目的地址、源地址、长度字段、数据字段,FCS共5部分
private String macHex = ""; // MAC帧16进制串
private String fcsHex = ""; // FCS十六进制串
// MAC帧初始长度(byte)为目的地址长度(6bytes)+源地址长度(6bytes)+长度字段(2bytes)+FCS长度(4bytes)
private int macByteLength = 18;
// 4位的二进制数串,0-15
String[] FOUR_BIT_BINARY_TABLE = {
"0000", "0001", "0010", "0011",
"0100", "0101", "0110", "0111",
"1000", "1001", "1010", "1011",
"1100", "1101", "1110", "1111"
}; // 开始
void begin() {
this.input();
this.dealWithDestinationAddress();
this.dealWithSourceAddress();
this.dealWithData();
this.dealWithLengthField();
this.mac += this.lengthField; // 将长度字段添加到MAC帧
this.mac += this.dataBinary; // 将数据字段添加到MAC帧
this.dealWithFcs();
this.dealWithMac();
}
// 输入数据
private void input() {
Scanner in = new Scanner(System.in);
String data = "";
while(data.equals("")) {
System.out.println("请输入传输的数据:");
data = in.nextLine();
this.data = data;
}
System.out.println("---------------------------------------------------------");
in.close();
}
// 显示信息
void output() {
System.out.println("MAC帧(2进制)为:");
System.out.println(this.mac);
System.out.println("目的地址为:");
System.out.println(this.destinationBinaryAddress);
System.out.println("源地址为:");
System.out.println(this.sourceBinaryAddress);
System.out.println("长度/类型(2进制)为:");
System.out.println(this.lengthField);
System.out.println("数据为:");
System.out.println(this.dataBinary);
System.out.println("FCS(2进制)为:");
System.out.println(this.fcs);
System.out.println("MAC帧(16进制)为:");
System.out.println(this.macHex);
System.out.println("FCS(16进制)为:");
System.out.println(this.fcsHex);
System.out.println("---------------------------------------------------------");
}
// 处理目的地址
private void dealWithDestinationAddress() {
this.destinationBinaryAddress = this.hexToBinary(this.DESTINATION_ADDRESS);
this.mac += this.destinationBinaryAddress; // 将目的地址(2进制)添加到MAC帧
}
// 处理源地址
private void dealWithSourceAddress() {
this.sourceBinaryAddress = this.hexToBinary(this.SOURCE_ADDRESS);
this.mac += this.sourceBinaryAddress; // 将源地址(2进制)添加到MAC帧
}
// 处理传输数据
private void dealWithData() {
int dataByteLength = 0; // 数据长度(byte)
this.dataBinary = this.dataToBinaryString(data);
dataByteLength = this.dataBinary.length() / 8;
if(dataByteLength < this.MIN_DATA_BYTES_LENGTH) {
this.dataBinary = this.fillZero(this.dataBinary, this.MIN_DATA_BITS_LENGTH, false);
dataByteLength = this.MIN_DATA_BYTES_LENGTH;
} else if(dataByteLength > this.MAX_DATA_BYTES_LENGTH) {
System.out.println("输入的数据超过范围!");
System.exit(0);
}
this.macByteLength += dataByteLength;
}
// 处理长度字段
private void dealWithLengthField() {
this.lengthField = Integer.toBinaryString(this.macByteLength);
this.lengthField = this.fillZero(this.lengthField, this.LENGTH_FIELD_BYTES * 8, true);
}
// 处理FCS
private void dealWithFcs() {
String dividend = this.fillZero(this.mac, this.mac.length() + this.FCS_BITS_LENGTH, false);
this.fcs = this.solveFcs(dividend, this.DIVISOR_P);
this.fcsHex = this.binaryToHex(this.fcs);
this.mac += this.fcs;
}
// 处理MAC帧
private void dealWithMac() {
this.macHex = this.binaryToHex(this.mac);
}
/* @return String 返回求解到的FCS串
* @param dividend 被除数,二进制
* @param divisor 除数,二进制
*/
private String solveFcs(String dividend, String divisor) {
String fcs = "";
char[] dividendArray = dividend.toCharArray();
char[] divisorArray = divisor.toCharArray();
// 指向被除数中用于当前运算的数据的首部和尾部,长度和除数相同
int head = 0; // 指向头部下标,第一次是0
int tail = head + divisor.length() - 1; // 尾部下标,第一次是32
int i;
while(tail < dividend.length()) {
if('1' == dividendArray[head]) {
for(i = head; i <= tail; ++i) {
if(dividendArray[i] == divisorArray[i - head]) {
dividendArray[i] = '0';
} else {
dividendArray[i] = '1';
}
}
}
++head;
++tail;
}
for(i = head; i < dividendArray.length; ++i) {
fcs += dividendArray[i];
}
return fcs;
}
/* @return String 二进制字符串
* @param data 字符串
* 将字符串转换成二进制字符串,通过ASCII转换
*/
private String dataToBinaryString(String data) {
char[] stringArray = data.toCharArray();
String result = "";
String oneBit = "";
for (int i = 0; i < stringArray.length; ++i) {
oneBit = Integer.toBinaryString(stringArray[i]);
oneBit = this.fillZero(oneBit, 8, true);
result += oneBit;
}
return result;
}
/* @return String 填充0之后的二进制串
* @param data 需要填充0的二进制串
* @param finalBitLength 填充0后的长度(位)
* @param way 填充0。way=true 首部填充0,way=false 尾部填充0
*/
private String fillZero(String data, int finalBitLength, boolean way) {
String result = "";
for(int i = data.length(); i < finalBitLength; ++i) {
result += '0';
}
if(true == way) {
result += data;
} else {
result = data + result;
}
return result;
}
/* @return String 返回转换后的二进制串
* @param hex 要转换的十六进制串
*/
private String hexToBinary(String hex) {
String binary = "";
int hexBitLength = hex.length();
for(int i = 0; i < hexBitLength; ++i) {
if(hex.charAt(i) >= '0' && hex.charAt(i) <= '9') {
binary += this.FOUR_BIT_BINARY_TABLE[hex.charAt(i) - '0'];
} else if(hex.charAt(i) >= 'a' && hex.charAt(i) <= 'z') {
binary += this.FOUR_BIT_BINARY_TABLE[hex.charAt(i) - 'a' + 10];
} else if(hex.charAt(i) >= 'A' && hex.charAt(i) <= 'Z') {
binary += this.FOUR_BIT_BINARY_TABLE[hex.charAt(i) - 'A' + 10];
}
}
return binary;
}
/* @return String 16进制串
* @param binary 需要转换成16进制的2进制串
*/
private String binaryToHex(String binary) {
String hex = "";
String fourBits = "";
int oneBit = 0;
int length = binary.length() / 4;
for(int i = 0; i < length; ++i) {
fourBits = binary.substring(0, 4);
oneBit = Integer.parseInt(fourBits, 2);
hex += Integer.toHexString(oneBit);
binary = binary.substring(4);
}
return hex;
}
}

测试类Test.java代码如下:

public class Test {
public static void main(String[] args) {
MacFrameEncapsulation f = new MacFrameEncapsulation();
f.begin();
f.output();
}
}

5、截图

MAC帧封装的更多相关文章

  1. LB(Load balance)负载均衡集群--{LVS-[NAT+DR]单实例实验+LVS+keeplived实验} 菜鸟入门级

    LB(Load balance)负载均衡集群 LVS-[NAT+DR]单实例实验 LVS+keeplived实验 LVS是Linux Virtual Server的简写,意即Linux虚拟服务器,是一 ...

  2. LVS-DR工作原理

    我们都知道LVS有LVS-DR,LVS-NAT,LVS-TUN三种模式,其中DR模式意为Direct Routing(直接路由).对于LVS-DR,你到底了解到什么程度?本文通过一个实例场景,详细介绍 ...

  3. 2. LVS/DR 配置

    平台:RedHat Enterprise Linux centos6.3       ipvsadm             ipvs 1.DR模型 DR模型:直接路由模型,每个Real Server ...

  4. LVS实战1

    (一).NAT模式:NAT模型:地址转换类型,主要是做地址转换,类似于iptables的DNAT类型,它通过多目标地址转换,来实现负载均衡:特点和要求: 1.LVS(Director)上面需要双网卡: ...

  5. 基于FPGA的以太网开发

    基于FPGA的以太网开发,在调试过的FPGA玩家开来,其实算不上很难的技术!但是如果只是菜鸟级别的选手,没有调试过的话,就有些头疼了!早在自己在实习的时候,就接触到XAUI(万兆以太网口)接口,但是由 ...

  6. 计算机网络中的帧封装(C实现)

    这段时间开始复习计算机网络,看到帧封装这一节,结合以前的课程设计,就用C写了个帧封装的程序,说实话C学的确实不怎么样,实现的时候对于文件操作那部分查了好多资料,下面说说帧封装是啥情况. 学过计算机网络 ...

  7. 转发表(MAC表)、ARP表、路由表总结

    原文:https://cloud.tencent.com/developer/article/1173761 转发表(MAC表).ARP表.路由表总结 我是东东东   发表于我是东东强订阅 1.5K ...

  8. 计算机网络参考模型,IP地址及MAC地址查看方法,数据包封装过程

    分层思想 首先,计算机网络参考模型,是基于分层思想而出现的.分层思想,就是将复杂流程分解为几个功能单一的子过程. 优点: 可以让整个流程更加清晰, 让复杂问题简单化, 更容易发现问题,并真对性的解决问 ...

  9. 总结:Mac前端开发环境的搭建(配置)

    新年新气象,在2016年的第一天,我入手了人生中第一台自己的电脑(大一时好友赠送的电脑在一次无意中烧坏了主板,此后便不断借用别人的或者网站的).macbook air,身上已无分文...接下来半年的房 ...

随机推荐

  1. 第十七周助教工作总结——NWNU李泓毅

    助教博客链接:https://www.cnblogs.com/NWNU-LHY/ 本次作业的要求:软件测试与ALPHA冲刺:https://www.cnblogs.com/nwnu-daizh/p/1 ...

  2. 三大框架 之 Hibernate框架概述(概述、配置、核心API)

    目录 Hibernate框架概述 什么是框架 hibernate简介(JavaEE技术三层架构所用到的技术) hibernate是什么框架 ORM hibernate好处 Hibernate基本使用 ...

  3. 和重复搭建开发环境说 Bye Bye 之Vagrant

    每每新同事入职,都要在自己电脑上配置一堆环境,费神费力:每每开发测试都要重新配置开发环境,手工搭建,步骤很繁琐,极易出错. 大神在时,大神搭建,大神不在,以手抚膺坐长叹.为此,VVVVVagrant横 ...

  4. windows powershell学习

    PowerShell,从名字可以知道,他首先是一个shell,shell的意思就是和Linux的bash等一样.和原来的cmd一样就是在里边敲命令(可执行文件)使用: 而Power就意味他是一个功能强 ...

  5. typescript装饰器 方法装饰器 方法参数装饰器 装饰器的执行顺序

    /* 装饰器:装饰器是一种特殊类型的声明,它能够被附加到类声明,方法,属性或参数上,可以修改类的行为. 通俗的讲装饰器就是一个方法,可以注入到类.方法.属性参数上来扩展类.属性.方法.参数的功能. 常 ...

  6. typescript接口扩展

    /* typeScript中的接口 接口扩展 */ /* 接口的作用:在面向对象的编程中,接口是一种规范的定义,它定义了行为和动作的规范,在程序设计里面,接口起到一种限制和规范的作用.接口定义了某一批 ...

  7. shell编程系列15--文本处理三剑客之awk格式化输出printf

    shell编程系列15--文本处理三剑客之awk格式化输出printf printf的格式说明符 格式符 含义 %s 打印字符串 %d 打印十进制数 %f 打印一个浮点数 %x 打印十六进制数 %o ...

  8. 关于xadmin的网址收集

    https://blog.csdn.net/yambo1992/article/details/80918250 https://www.colabug.com/4728510.html django ...

  9. Laya发布微信小游戏项目

    版本2.1.1.1 创建项目时,勾选微信/百度小游戏bin目录快速调试 发布项目时,选择发布平台为微信小游戏 用微信开发者工具打开release/wxgame,这就是微信小游戏项目了,很方便

  10. Flink 自定义source和sink,获取kafka的key,输出指定key

    --------20190905更新------- 沙雕了,可以用  JSONKeyValueDeserializationSchema,接收ObjectNode的数据,如果有key,会放在Objec ...