问题:如何在代码层面封装协定细节?如何将接收缓冲区中的数据解析为 Message ?

深度思考

数据是否可能解析成为 Message ?
  • 数据量足够

    • 如果数据量足够,是否可能解析不止一个 Message?
    • 如何解决残余数据 (属于下一个 Message)
  • 数据量有余

    • 是否达到协定最小长度(12 字节)?
    • 如何解决数据量超过最小长度,但不足以创立一个 Message 的状况?

初步的解决方案

  • 定义一个模块用于从字节流解析 Message
  • 可 从指定内存 或 从指定文件描述符 读取并解析
  • 当至多存在 12 个字节时开始解析

    • 首先解析协定中的头信息和数据区长度(length)
    • 依据数据区长度持续从字节流读取数据(payload)
    • 当协定数据解析实现时,创立 Message 并返回,否则,返回 NULL

协定解析模块的初步设计

解析器接口定义
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);
解析器数据结构
typedef struct msg_parser {    Message cache;    // 缓存已解析的音讯头    int header;        // 标识音讯头是否解析胜利    int need;        // 标识还剩多少字节能力实现解析    Message *msg;    // 解析中的协定音讯(半成品)}MsgParser;
条件:内存长度至多间断 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 = ntohs(p->cache.length);mem += p->need;length -= p->need;p->header = 1;p->need = p->cache.length;
从内存中读取 payload 中的数据(可读取屡次)
if (!p->msg) {  // 胜利创立音讯头之后, 创立 Message    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;}

编程试验:协定解析模块初步设计

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 *parse);void MParser_Del(MParser *parse);#endif
msg_parser.c
#include <malloc.h>#include <string.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(){    MsgParser *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) {        return ret;    }    if (!p->header) {        if (p->need <= length) {            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;            ret = MParser_ReadMem(parser, mem, length);        }    } else {        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, 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;    return ret;}void MParser_Reset(MParser *parse){    MsgParser *p = (MsgParser*)parse;    if (p) {        p->header = 0;        p->need = sizeof(p->cache);        free(p->msg);        p->msg = NULL;    }}void MParser_Del(MParser *parse){    MsgParser *p = (MsgParser*)parse;    if (p) {        free(p->msg);        free(p);    }}
test.c
#include <stdio.h>#include <unistd.h>#include <stdlib.h>#include "msg_parser.h"int main(){       MParser *p = MParser_New();    char buf[] = {0x00, 0x01, 0x00, 0x02, 0x00, 0x03, 0x00, 0x04, 0x00, 0x00, 0x00, 0x04};    char data[] = {0x11, 0x12, 0x13, 0x14};    Message *m = MParser_ReadMem(p, buf, sizeof(buf));    int i = 0;    if (!m) {        printf("parse again...\n");        m = MParser_ReadMem(p, data, sizeof(data));    }    printf("m = %p\n", m);    if (m) {        printf("type = %d\n", m->type);        printf("cmd = %d\n", m->cmd);        printf("index = %d\n", m->index);        printf("total = %d\n", m->total);        printf("length = %d\n", m->length);        for (i=0; i<m->length; ++i) {            printf("0x%02x ", m->payload[i]);        }        printf("\n");        free(m);    }    MParser_Del(p);    return 0;    }

思考:如何通过 socket 文件描述符实时解析协定数据?