应用协议解析模块(上)
1、问题
如何在代码层面封装协议细节?
如何将接收缓冲区中的数据解析成为Message?
1.1深度思考
数据是否能够解析成为Message?
- 数据量足够
- 如果数据量足够,是否能够解析不止一个Messge?
- 如何处理剩余数据(属于下一个Message)?
- 数据量不足
- 是否达到协议最小长度(12字节)?
- 如何处理数据量超过最小长度,但不足以创建一个Message的情况?
2、初步解决方案
- 定义一个模块用于从字节流解析Message
- 可从指定内存或从指定文件描述符读取并解析
- 当至少存在12个字节时开始解析
- 首先解析协议中的头信息和数据区长度(length)
- 根据数据区长度继续从字节流读取数据(payload)
- 当协议数据解析完成时,创建Message并返回,否则,返回NULL
3、协议解析模块的初步设计
3.1解析器接口定义
typedef void MParser;
MParser* MParser_New();
Message* MParser_ReadMem(MParser* parser, unsigned char* mem, unsigned int length);
Message* MParser_ReadFd(MParser* parser, int fd);
void MParser_Reset(MParser* parser);
void MParser_Del(MParser* parser);
3.2解析器数据结构
typedef struct msg_parser
{
Message cache; //缓存已解析的消息头
int header; //标识消息头是否解析成功
int need; //标识还需多少字节才能完成解析
Message* msg; //解析中的协议消息(半成品)
}MsgParser;
3.3从内存中解析协议数据
- 条件:内存长度至少连续12个字节
memcpy(&p->cache, mem, p->need);
p->cache.type = ntos(p->cache.type);
p->cahce.cmd = ntos(p->cahce.cmd);
p->cache.index = ntos(p->cache.index);
p->cahce.total = ntos(p->cahce.total);
p->cahce.length = ntol(p->cahce.length);
mem += p->need;
//感觉length,应该是p->cahce.length
length -= p->need;
p->header = 1;
p->need = p->cache.length;
- 从内存中读取payload中的数据(可多次读取)
if( !p->msg )
{
p->msg = malloc(sizeof(p->cache) + p->need);
if( p->msg )
{
*p->msg = p->cache;
}
}
if( p->msg )
{
unsigned int len = (p->need < length) ? p->need : length;
unsigned int offset = p->msg->length - p->need;
memcpy(p->msg->payload + offset, mem, len);
p->need -= len;
}
4、编程实验
//msg_parser.c
#include <string.h>
#include <malloc.h>
#include <arpa/inet.h>
#include <unistd.h>
#include "msg_parser.h"
typedef struct msg_parser
{
Message cache; //缓存已解析的消息头
int header; //标识消息头是否解析成功
int need; //标识还需多少字节才能完成解析
Message* msg; //解析中的协议消息(半成品)
}MsgParser;
MParser* MParser_New()
{
MParser* ret = calloc(1, sizeof(MsgParser));
MParser_Reset(ret);
return ret;
}
Message* MParser_ReadMem(MParser* parser, unsigned char* mem, unsigned int length)
{
Message* ret = NULL;
MsgParser* p = (MsgParser*)parser;
if( p && mem && length)
{
if( !p->header )//=0,解析器状态:还没解析
{
if( p->need <= length)//至少12字节,否则不解析
{
memcpy(&p->cache, mem, p->need);
p->cache.type = ntohs(p->cache.type);
p->cache.cmd = ntohs(p->cache.cmd);
p->cache.index = ntohs(p->cache.index);
p->cache.total = ntohs(p->cache.total);
p->cache.length = ntohl(p->cache.length);
mem += p->need;
length -= p->need;
p->header = 1;//状态转换
p->need = p->cache.length;//注意,不是length
ret = MParser_ReadMem(p, mem, length);//递归调用
}
}
else
{
if( !p->msg )
{
p->msg = malloc(sizeof(p->msg) + p->need);
if( p->msg )
{
*p->msg = p->cache;//填充固定部分
}
}
if( p->msg )
{
unsigned int len = (p->need < length) ? p->need : length;
unsigned int offset = p->msg->length - p->need;
memcpy(p->msg->payload, mem, len);
p->need -= len;
}
if( !p->need )//半成品成为成品
{
ret = p->msg;
p->msg = NULL;
MParser_Reset(p);
}
}
}
return ret;
}
Message* MParser_ReadFd(MParser* parser, int fd)
{
Message* ret = NULL;
MsgParser* p = (MsgParser*)parser;
}
void MParser_Reset(MParser* parser)
{
MsgParser* p = (MsgParser* )parser;
if( p )
{
p->header = 0;
p->need = sizeof(p->cache);
if( p->msg )
{
free(p->msg);
}
p->msg = NULL;
}
}
void MParser_Del(MParser* parser)
{
MsgParser* p = (MsgParser* )parser;
if( p )
{
if( p->msg )
{
free(p->msg);
free(p);
}
}
}
//msg_parser.h
#ifndef _MSG_PARSER_H_
#define _MSG_PARSER_H_
#include "message.h"
typedef void MParser;
MParser* MParser_New();
Message* MParser_ReadMem(MParser* parser, unsigned char* mem, unsigned int length);
Message* MParser_ReadFd(MParser* parser, int fd);
void MParser_Reset(MParser* parser);
void MParser_Del(MParser* parser);
#endif// _MSG_PARSER_H_
//test.c
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <malloc.h>
#include "msg_parser.h"
int main(void)
{
MParser* p = MParser_New();
char buff[] = {0x00,0x01,0x00,0x02,0x00,0x03,0x00,0x04,0x00,0x00,0x00,0x04};
char data[] = {0x11,0x22,0x33,0x44};
Message* m = MParser_ReadMem(p, buff, sizeof(buff));
int i = 0;
if(!m)
{
printf("m parser again\n");
m = MParser_ReadMem(p, data, sizeof(data));
}
if(m)
{
printf("m->type = %d\n",m->type);
printf("m->cmd = %d\n",m->cmd);
printf("m->index = %d\n",m->index);
printf("m->total = %d\n",m->total);
printf("m->length = %d\n",m->length);
for(i = 0; i < m->length; i++)
{
printf("0x%02x\n",m->payload[i]);
}
}
free(m);
MParser_Del(p);
return 0;
}
5、思考
如何通过socket文件描述符实时解析协议消息?