?? 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 + -