📄 frameparser.cpp
字号:
/*******************************************************************************
* 文件名称:FrameParser.cpp
* 摘 要:通过打开已封装好多个以太包帧结构的文件进行以太网帧结构解析并打印
* 单 位:软件学院
* 作 者:姚旺
* 学 号:2120070369
* 完成日期:2006年10月23日
*******************************************************************************/
/******************
* 头文件列表:
******************/
#include "stdafx.h"
/******************
* 定义常量:
******************/
#define CORRECT 0 //用于CRC效验正确时的返回值
/**********************
* 引用的命名空间列表:
**********************/
using namespace System;
/****************************
* 全局变量声明及初始化列表:
****************************/
int g_nCount=0; //全局变量g_nCount记录帧个数
/*******************
* 函数声明列表:
*******************/
void fnReadFrame(IO::FileStream ^fsFile); //读一帧并操作
void fnWriteArrayBytes(array<Byte> ^byBytes,bool bDash); //打印二进制形式数据组
Byte fnCRCcheck(array<Byte> ^byPCrc,Byte byCRCcode); //CRC校验
Byte fnBinaryDiv(array<Byte> ^byPDiv); //二进制除法
/*******************
* 函数定义列表:
*******************/
/**********************************************************************
* 主函数主要通过调用fnReadFrame函数实现程序的“以太网帧结构解析”功能。
*
* 输入参数:
* args 命令行的参数数组。
*
**********************************************************************/
int main(array<String ^> ^args)
{
/*******************************************************
* 命令行参数检测:
* 参数是否正确。
*
********************************************************/
if(args->Length != 1) //判断参数数量,不正确则退出
{
Console::WriteLine("参数不正确,正确格式为FrameParser [inputfile]。\n请按任意键退出...");
Console::ReadKey(true); //等待用户任意键继续
return 0;
}
if(!IO::File::Exists(args[0])) //判断参数所指定文件是否存在,不存在则退出
{
Console::WriteLine("文件{0}不存在。\n请按任意键退出...",args[0]);
Console::ReadKey(true); //等待用户任意键继续
return 0;
}
/****************************
* 变量声明:
****************************/
IO::FileStream^ fsFile; //参数指定的被打开文件流
/**************************************************************************
* 主语句部分:写入try...catch...finally语句中以增强对错误的处理能力。
* 1.try部分打开文件并调用fnReadFrame函数实现程序功能。
* 2.catch部分对错误和程序运行中产生的throw进行处理输出错误信息。
* 3.finally部分结束程序并已打开的关闭文件。
*
**************************************************************************/
try //当运行中发现错误或产生throw时转到catch
{
fsFile=gcnew IO::FileStream(args[0],IO::FileMode::Open);
//打开文件流为参数指定文件
while(fsFile->Position!=fsFile->Length){
g_nCount++; //每开始读一个帧,帧数量加1
fnReadFrame(fsFile); //调用fnReadFrame函数对当前位置的帧进行读取等操作
}
}
catch(::Exception^ e)
{
Console::WriteLine();
Console::WriteLine(e->Message); //打印出错误细节
}
finally
{
Console::WriteLine("请按任意键退出...");
Console::ReadKey(true); //等待用户任意键继续
fsFile->Close(); //关闭文件
}
return 0;
}
/**************************************************************************
* 本函数按要求打印bytes中字段为十六进制形式。
*
* 输入参数:
* byBytes 被打印的8位无符号数据组。
* bDash 是否在数据组间打印符号“-”。
*
**************************************************************************/
void fnWriteArrayBytes(array<Byte> ^byBytes,bool bDash)
{
/******************************************************************
* 按byBytes数组个数循环:每次循环以十六进制形式打印一个字节的数据。
******************************************************************/
for(short int i=0;i<byBytes->Length;i++)
{
Console::Write("{0:X2}"+(bDash?"-":" "),byBytes[i]); //结合bDash判断打印当前一个字节
}
Console::Write("\b \n"); //消除最后一个bDash或空格并换行
}
/**************************************************************************
* 本函数进行二进制除法,将参数pDiv除以生成式10000111并返回余数。
*
* 输入参数:
* byPDiv 进行二进制除法的被除数。
*
* 返回类型:
* Byte 二进制除法完成后的余数。
*
**************************************************************************/
Byte fnBinaryDiv(array<Byte> ^byPDiv)
{
/************************
* 变量声明及初始化列表:
************************/
Byte byCrc=0; //定义Byte型存放余数
/*******************************************************
* 根据被除数长度进行除法循环:
1.每次循环读取一个字节进行处理。
2.对这一个字节处理要进行8次循环。
3.当前字节处理完后若后面还有数据,回到步骤1,否则到4。
4.全部完成后结束本函数,返回余数值byCrc。
*******************************************************/
for(int i=0;i < byPDiv->Length;i++)
{
for(Byte j=(Byte) 0x80;j>0;j>>=1) //进行8次循环,以完成一个字节的操作
{
if(byCrc&0x80) //高位为1则进行除法
{
byCrc<<=1; //左移一位
if(byPDiv[i]&j) byCrc^=0x01; //将输入数据相应位的值递补到余数末位
byCrc^=0x07; //除法运算
}
else
{
byCrc<<=1; //左移一位
if(byPDiv[i]&j) byCrc^=0x01; //将输入数据相应位的值递补到余数末位
}
}
}
return byCrc; //返回余数值
}
/***************************************************************************
* 本函数进行CRC校验,校验过程中通过调用fnBinaryDiv函数实现二进制除法。
*
* 输入参数:
* byPCrc 被校验的目标字段(不包括CRC校验码本身)。
* byCRCcode 帧中得到的CRC校验码。
*
* 返回类型:
* Byte 若CRC校验正确则返回CORRECT,否则返回正确的CRC校验码。
*
***************************************************************************/
Byte fnCRCcheck(array<Byte> ^byPCrc,Byte byCRCcode)
{
/************************
* 变量声明及初始化列表:
************************/
array<Byte> ^byPDiv=gcnew array<Byte>(byPCrc->Length+1); //初始化新的Byte数组存
byPCrc->CopyTo(byPDiv,0); //放byPCrc和byCRCcode
byPDiv[byPCrc->Length]=byCRCcode; //合并的数组以进行校验
/**************************************************************************
* 主语句部分:
*
* 1.调用fnBinaryDiv对byPDiv进行二进制除法。
* 2.若有余数则接受的帧不正确,并返回正确的CRC检验码;否则返回CORRECT。
*
**************************************************************************/
if(fnBinaryDiv(byPDiv))
{
byPDiv[byPCrc->Length]=0; //byPDiv数组最后一个字节置零以存放正确的CRC校验码
return fnBinaryDiv(byPDiv); //算出正确的CRC校验码并返回
}
else return CORRECT; //若无余数则帧正确
}
/**********************************************************************
* 本函数对当前位置的帧进行读取,打印各字段,判断CRC等操作。
*
* 输入参数:
* fsFile 被读取的文件流。
*
**********************************************************************/
void fnReadFrame(IO::FileStream ^fsFile)
{
/************************
* 变量声明及初始化列表:
************************/
array<String ^> ^szErrors={ //初始化各种错误信息
"异常:已读写到文件尾,后续数据丢失。"};
array<String ^> ^szHeaders={ //初始化各标题行字符串
"前导码:","帧前定界符:","目的地址:",
"源地址:","长度字段:","数据字段:",
"CRC校验(正确):","CRC校验(错误):",
"状态:Accept","状态:Discard","序号:",
"应为:"};
array<int> ^nCodeCount={7,1,6,6,2}; //初始化int数组存放对应szHeaders数组前五次读取时应读取的字节数量
array<Byte> ^byBytes; //用于存放每次读取的内容的字节型数组
int nPStart=0,nPLength=0; //用于记录进行CRC效验的字段内容在文件中的开始位置和长度
int nContextLen=0; //数据字段长度
array<Char>^ cChars; //数据字段内容
array<Byte>^ byCRCcode=gcnew array<Byte>(1); //帧中读取的CRC效验码
Byte byNCRCcode; //值为0时标记byCRCcode正确,否则存放重新校验产生的正确CRC效验码
/**************************************************************************
* 主语句部分:
*
* 1.打印“序号”行。
* 2.for循环中调用fnWriteArrayBytes函数打印当前帧的“序号”行后的五行。
* 3.打印“数据字段”行。
* 4.读取CRC码并调用fnCRCcheck函数校验,并打印“CRC校验”行。
*
**************************************************************************/
Console::WriteLine("{0}{1}",szHeaders[10],g_nCount); //打印当前帧第一行“序号”行
/**************************************************************************
* for循环:调用fnWriteArrayBytes函数打印当前帧的“序号”行后的五行:
* "前导码:","帧前定界符:","目的地址:","源地址:","长度字段:"。
**************************************************************************/
for(Byte i=0;i<5;i++)
{
if(i==2) nPStart=(int)fsFile->Position; //开始读"目的地址"时记录nPStart
byBytes=gcnew array<Byte>(nCodeCount[i]); //创建以当前行要读入字节数为数组数量的新的byBytes实例
if(fsFile->Read(byBytes,0,nCodeCount[i])<byBytes->Length)
//从文件中读取nCodeCount[i]个字节填充byBytes
{
throw gcnew IO::EndOfStreamException(szErrors[0]); //若读取字节数不足则抛出异常
}
Console::Write(szHeaders[i]); //打印相应标题
fnWriteArrayBytes(byBytes,1<i && i<4); //调用fnWriteArrayBytes函数以打印相应十六进制形式字段
}
/**************************************************************************
* 打印“数据字段”行。
**************************************************************************/
nContextLen=Convert::ToChar(byBytes[0])*256+
Convert::ToChar(byBytes[1]); //计算数据字段长度
Console::Write(szHeaders[5]); //打印数据字段标题
byBytes=gcnew array<Byte>(nContextLen);
if(fsFile->Read(byBytes,0,nContextLen)<byBytes->Length) //将数据字段内容读入新的byBytes实例
{
throw gcnew IO::EndOfStreamException(szErrors[0]); //若读取字节数不足则抛出异常
}
Text::Decoder^ dcTmp =
Text::Encoding::Default->GetDecoder(); //获取系统当前ANSI代码页的编码的解码器
cChars= gcnew array<Char>(nContextLen);
dcTmp->GetChars(byBytes, 0, byBytes->Length, cChars, 0 );
//将byBytes中数据字段内容解码为当前系统ANSI代码后存入cChars中
for(int i=0;i<nContextLen;i++) //打印数据字段内容
{
Console::Write("{0}",cChars[i]);
}
Console::WriteLine();
/**************************************************************************
* 读取CRC码并调用fnCRCcheck函数校验,并打印“CRC校验”行。
**************************************************************************/
if(46-nContextLen>0) fsFile->Seek(46-nContextLen,IO::SeekOrigin::Current);
//若数据字段长度不足最小的46B则读完剩下的,使文件指针指向CRC效验码
nPLength=(int)fsFile->Position-nPStart; //计算nPLength
int nTmp=fsFile->ReadByte(); //读入CRC校验码到临时变量nTmp中
if(nTmp==-1) //若已到文件尾,即没有正确读入则
{
throw gcnew IO::EndOfStreamException(szErrors[0]); //抛出异常
}
else
{
byCRCcode[0]=(Byte)nTmp; //否则将nTmp赋值到byCRCcode
}
byBytes=gcnew array<Byte>(nPLength); //从文件读
fsFile->Seek(nPStart,IO::SeekOrigin::Begin); //入被进行CRC
fsFile->Read(byBytes,0,nPLength); //校验的字段
fsFile->Position=nPStart+nPLength+1; //读文件指针恢复到当前帧结束位置
byNCRCcode=fnCRCcheck(byBytes,byCRCcode[0]); //检验CRC并返回正确时值为0或错误时为正确CRC值
if(byNCRCcode) //若CRC错误进行的打印
{
Console::WriteLine("{0}{1:X2} {2}{3:X2}",szHeaders[7],byCRCcode[0],szHeaders[11],byNCRCcode);
Console::WriteLine(szHeaders[9]);
}
else //若CRC正确进行的打印
{
Console::WriteLine("{0}{1:X2}",szHeaders[6],byCRCcode[0]);
Console::WriteLine(szHeaders[8]);
}
Console::WriteLine();
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -