通过控制台输入一段文字,输出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. Percona,MariaDB,MySQL衍生版如何取舍

    缘起 自从甲骨文公司收购了MySQL后,有将MySQL闭源的潜在风险.而且Oracle对培养MySQL这个免费的儿子并不太用心,漏洞修补和版本升级的速度一段时间非常缓慢,所以业界对MySQL的未来普遍 ...

  2. Mac和window生成ssh和查看ssh key

    一.MAC系统 mac 系统开始就已经为我们安装了ssh 如果没有安装,首先安装 打开终端:$ ssh -v 查看ssh版本 OpenSSH_6.2p2, OSSLShim 0.9.8r 8 Dec ...

  3. QtCore概述

    所有其他Qt模块都依赖于这个模块. 要包含模块类的定义,请使用以下指令: include < QtCore > 如果您使用qmake来构建您的项目,则默认将QtCore包含在内. 核心功能 ...

  4. JEECG MiniDao优劣

    Minidao 1.5.1 - 我认为这是一个不严谨的错误 - andotorg的个人空间 - OSCHINAhttps://my.oschina.net/antsdot/blog/1800832 M ...

  5. 004-行为型-02-模板方法模式(Template Method)

    一.概述 定义了一个算法的骨架,并允许子类为一个或多个步骤提供实现.模板方法使得子类可以在不改变算法结构的情况下,重新定义算法的某些步骤 1.1.适用场景 一次性实现一个算法的不变的部分,并将可变的行 ...

  6. Spring cloud微服务安全实战-6-9sentinel之熔断降级

    来讲一下降级规则 服务会互相调用,服务A会有一些服务之间的依赖. 假设服务D的响应时间变长了.A调用D就会卡住了. 熔断,某一个服务出现问题,会把服务拖死.如果A出现,会把依赖A的那些服务拖死. 主要 ...

  7. spring 使用@Bean装配Bean

    通过@Component装配Bean,但是@Component只能注解在类上,不能注解到方法上.对于Java而言,大部分的开发都需要引入第三方的包(jar文件),而且往往并没有这些包的源码,这时候将无 ...

  8. SqlServer里,一条sql进行递归删除

    Server 2005中提供了公用表表达式(CTE),使用CTE,可以使SQL语句的可维护性,同时,CTE要比表变量的效率高得多. 存储过程方法: create proc up_delete_ncla ...

  9. e.target 和 e.currentTarget

    们可以得出: e.currentTarget指的是注册了事件监听器的对象,而e.target指的是该对象里的子对象,也是触发这个事件的对象!这么说应该明白 了吧?

  10. 关于Js异常

    一.Javascript的异常处理机制 当javascript代码中出现错误的时候,js引擎就会根据js的调用栈逐级寻找对应的catch,如果没有找到相应的catch handler或catch ha ...