1. 程式人生 > 其它 >一個簡單的工業乙太網協議實現

一個簡單的工業乙太網協議實現

技術標籤:C語言之旅

文章目錄


前言

基於winpcap和C

提示:以下是本篇文章正文內容,下面案例可供參考

一、初步思想

首先需要定義資料幀的格式,這裡為了簡便設為:
目的MAC(6位元組)+源MAC(6位元組)+型別(2位元組)+資料+CRC32(4位元組)

在這裡插入圖片描述
為了傳送和接受資料幀,設定兩個結構體,一個用來發送資料幀(給底層的),一個用來儲存資料幀(給上層或使用者的)

還需要設定兩個結構體之間的轉換函式(封裝和解封)

二、結構體設計

1.上層

上層按照資料幀的欄位設計即可

text就是要傳送的資料,為了節約空間設定成一個字元指標,在使用時分配空間
設定兩個CRC32,一個用於儲存計算的CRC32,一個用來接收資料幀中的CRC32,用來判斷資料在傳輸過程是否發生改變

typedef struct ether_header{
	u_int8_t dhost[6];
	u_int8_t shost[6];
	unsigned short type;
}ETHHEADER,*PETHHEADER;

typedef struct ether{
	ETHHEADER header;
	char *text;
	int textsize;
	u_int32_t CRC32,
CRC32_Recv; }ETHER,*PETHER;

2.底層

就是把上層(使用者)的結構體按位元組儲存到一個字串中,size是總共要傳送的資料位元組大小,data也是在使用中分配,這裡儲存的CRC32用於使用者輸出、檢視

傳送的時候呼叫winpcap中的函式傳送

typedef struct frame{
	char *data;
	int size;
	u_int32_t CRC32;
}FRAME,*PFRAME;

三、初始化函式

用於使用者設定資料頭

ETHHEADER  creat_ethheader(char *dhost,char *shost,u_int16_t type)
{ ETHHEADER ethhdr; for(int i=0;i<6;i++){ ethhdr.dhost[i]=dhost[i]; ethhdr.shost[i]=shost[i]; } ethhdr.type=type; return ethhdr; }

四、上層和頂層之間的轉換函式

實現CRC32計算的程式碼

u_int32_t crc32_table[256];
void generate_crc32_table()
{
	int i, j;
	u_int32_t crc;
	for (i = 0; i < 256; i++)
	{
		crc = i;
		for (j = 0; j < 8; j++)
		{
			if (crc & 1)
				crc = (crc >> 1) ^ 0xEDB88320;
			else
				crc >>= 1;
		}
		crc32_table[i] = crc;
	}
}

u_int32_t calculate_crc(u_int8_t *buffer, int len){
	generate_crc32_table();
	int i;
	u_int32_t crc;
	crc = 0xffffffff;
	for (i = 0; i < len; i++)
	{
		crc = (crc >> 8) ^ crc32_table[(crc & 0xFF) ^ buffer[i]];
	}
	crc ^= 0xffffffff;
	return crc;
}

封裝資料
(上層轉換為下層(兩個函式作用一樣,只是引數不一樣))
要點:要判斷資料欄位是否在46和1500位元組之間,還有傳過來字串不要直接使用(可能會越界訪問)

FRAME* ether_pack(PETHHEADER pethheader,char *matadata,int size){
	PFRAME pf=(PFRAME)malloc(sizeof(FRAME));
	char data[1600];
	int i;
	for(i=0;i<size;i++) data[i]=matadata[i];
	if(size<46){
		for(;i<45;i++) data[i]=0;
		data[45]=46-size;
		size=46;
	}else if(size>1500){
		printf("資料部分過長,大於1500B,失敗");
		return NULL;
	}

	pf->size=size+18; //6+6+2+4=18
	pf->data=(char *)malloc(pf->size);
	for(i=0;i<(pf->size)-4;i++){
		if(i<6)	pf->data[i]=pethheader->dhost[i];
		if(i>=6&&i<12) pf->data[i]=pethheader->shost[i];
		if(i>=12&&i<14) pf->data[i]=(pethheader->type)>>((13-i)*8);
		if(i>=14) pf->data[i]=data[i-14];
	}
	u_int32_t crc32=calculate_crc(pf->data,(pf->size)-4);
	pf->CRC32=crc32;
	for(;i<pf->size;i++){
		pf->data[i]=crc32>>(pf->size-1-i)*8;
	}
	return pf;
}

FRAME* ether_pack_one(PETHER pether){
	return ether_pack(&(pether->header),pether->text,pether->textsize);
}

解封資料(下層到上層)
這裡沒有進行CRC32的比較

PETHER ether_unpack(PFRAME pf){
	PETHER pe=(PETHER)malloc(sizeof(ETHER));
	int i;
	for(i=0;i<6;i++){
		pe->header.dhost[i]=pf->data[i];
	}
	for(;i<12;i++){
		pe->header.shost[i]=pf->data[i];
	}
	pe->textsize=pf->size-18;
	pe->text=(char *)malloc(pe->textsize);
	for(;i<12+pe->textsize;i++){
		pe->text[i-12]=pf->data[i];
	}
	pe->CRC32_Recv=0;
	for(;i<pf->size;i++){
		pe->CRC32_Recv=pe->CRC32_Recv|pf->data[i];
		if(i!=pf->size-1) pe->CRC32_Recv=pe->CRC32_Recv<<8;
	}
	u_int32_t crc=calculate_crc(pf->data,pf->size-4);
	pe->CRC32=crc;
	return pe;
}