通过控制台输入一段文字,输出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. 面向SOA服务架构的案例分析的研究

    随着互联网应用的不断发展,网络业务的种类.数量不断增加,计算机网络管理的研究重 点正在由过去的个别资源监控.应用可用性阶段,向着如何通过网络获得所需业务.业务流程的优化.保障业务服务水平方向发展.但这 ...

  2. cmake 手册详解【转】

    https://www.cnblogs.com/coderfenghc/tag/cmake/   CMake 手册详解(二十三) SirDigit 2012-12-16 22:03 阅读:11058 ...

  3. vue+springboot上传和下载附件功能

    https://blog.csdn.net/qq_35867245/article/details/84325385 上传附件(服务端代码) 第一步:在application.yml中配置附件要上传的 ...

  4. 新一代互联网传输协议QUIC

    QUIC(Quick UDP Internet Connections,快速UDP互联网连接)是Google提出的一种基于UDP改进的通信协议,其目的是降低网络通信的延迟,提供更好的用户互动体验. Q ...

  5. 阿里物联网平台(一)Windows系统+VS2017 模拟设备端接入

    https://blog.csdn.net/panwen1111/article/details/88365636 一.阿里物联网平台 平台地址:https://account.aliyun.com ...

  6. pip install失败报错解决方案

    cmd pip install 某些包时报错 pip install Consider using the `--user` option or check the permissions. 只需要p ...

  7. JEECG MiniDao优劣

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

  8. Flutter StatefulWidget 有状态组件、页面上绑定数据、改变页面数据

    在 Flutter 中自定义组件其实就是一个类,这个类需要继承 StatelessWidget/StatefulWidget. StatelessWidget 是无状态组件,状态不可变的 widget ...

  9. ISO/IEC 9899:2011 条款6.5.10——按位与操作符

    6.5.10 按位与操作符 语法 1.AND-expression: equality-expression AND-expression    equality-expression 约束 2.这些 ...

  10. SpringBoot启动嵌入式tomcat源码解读

    一.SpringBoot自动拉起Tomcat SpringBoot框架是当前比较流行的java后端开发框架,与maven结合大大简化了开发人员项目搭建的步骤,我们知道SpringBoot的启动类启动后 ...