应用协议解析模块(上)

本文详细探讨了如何在代码层面封装协议解析细节,包括数据量足够和不足时的处理策略,以及如何通过MParser模块从内存和文件描述符读取数据,构建Message。作者提供了关键代码实现和设计思路,适合协议开发者深入理解协议解析过程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

应用协议解析模块(上)

1、问题

如何在代码层面封装协议细节?

如何将接收缓冲区中的数据解析成为Message

1.1深度思考

数据是否能够解析成为Message?

  • 数据量足够
    • 如果数据量足够,是否能够解析不止一个Messge?
    • 如何处理剩余数据(属于下一个Message)?
  • 数据量不足
    • 是否达到协议最小长度(12字节)?
    • 如何处理数据量超过最小长度,但不足以创建一个Message的情况?

2、初步解决方案

  • 定义一个模块用于从字节流解析Message
  • 可从指定内存或从指定文件描述符读取并解析
  • 至少存在12个字节时开始解析
    1. 首先解析协议中的头信息和数据区长度(length)
    2. 根据数据区长度继续从字节流读取数据(payload)
    3. 当协议数据解析完成时,创建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文件描述符实时解析协议消息?

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值