应用协议解析模块(下)

应用协议解析模块(下)

1、问题

如何通过socket文件描述符实时解析协议消息?

2、深度思考

文件描述符是否能够获取足够的数据?

  • 数据量足够

    • 读取12字节解析消息头
    • 读取数据填充payload(length)
  • 数据量不足

    • 无法获取消息头所需数据(如何处理?解析状态如何切换?)
    • 无法获取payload完整数据(如何处理?是否追加?)

3、解决方案

策略:尽力获取数据,实时解析

  • 即便当前获取1字节,也可根据状态进行解析
  • 支持不同数据源多次接力解析(从内存或文件描述符交替获取数据)

充分利用解析器状态信息是实现解决方案的关键!!


4、解析器状态切换

image-20211229112338106

  • 状态切换函数
static void InitState(MsgParser* p)
{
    p->header = 0;
    p->need = sizeof(p->cache);
    
    free(p->msg);
    
    p->msg = NULL;
}
static int ToMidState(MsgParser* p)
{
	p->header = 1;
    p->need = p->cache.length;
    
    p->msg = malloc(sizeof(p->cache) + p->need);
    if( p->msg )
    {
        *p->msg = p->cache;
    }
    return !!p->msg;
}
static Message* ToLastState(MsgParser* p)
{
    Message* ret = NULL;
    
    if( p->header && !p->need )
    {
        ret = p->msg;
        p->msg = NULL;
    }
    return ret;
}

5、从文件描述符中获取数据

static int ToRecv(int fd, char* buf, int size)
{
    int retry = 0;
    int i = 0;
    
    while( i < size )
    {
        int len = read(fd, buf + i, size - i);
        if( len > 0 )
        {
            i += len;
        }
        else
        {
            if( retry++ > 5 )
            {
                break;
            }
            usleep(200 * 1000);
        }
    }
    return i;
}

6、从文件描述符中实时解析消息头

if( !p->header )
{
    int offset = sizeof(p->cahce) - p->need;
    //1.计算存放位置并读取消息头数据
    int len = ToRecv(fd, (char*)&p->cache + offset, p->need);
	if( len == p->need )
    {
        ntoh(&p->cache);
        //2.切换状态并读取payload数据
        if( ToMidState(p) )
        {
            ret = Mpaser_ReadFd(p, fd);
        }
    }
    else
    {
        p->need -= len;
    }
}

7、从文件描述符中获取payload数据

if( p->msg )
{
    int len = ToRecv(fd, p->msg->payload, p->need);
    p->need -= len;
}
//尝试切换到最终态,如果成功,则可获取协议消息,之后切换到初始态
if( ret = ToLastState(p) )
{
    InitState(p);
}

8、编程实战

//message.h
#ifndef _MESSAGE_DITAI_H_
#define _MESSAGE_DITAI_H_

typedef struct message
{
    unsigned short type;     //消息类型
    unsigned short cmd;      //命令
    unsigned short index;    //标识
    unsigned short total;    //总数
    unsigned int length;     //数据区长度
    unsigned char payload[]; //数据区(柔性数组)
}Message;

Message* Message_New(unsigned short type,
                    unsigned short cmd,
                    unsigned short index,
                    unsigned short total,
                    const char* payload,
                    unsigned int length);
                    

#endif
//message.c
#include "message.h"
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

Message* Message_New(unsigned short type,
                    unsigned short cmd,
                    unsigned short index,
                    unsigned short total,
                    const char* payload,
                    unsigned int length)
{
    Message* ret = malloc(sizeof(Message) + length);
    if(ret)
    {
        ret->type = type;
        ret->cmd = cmd;
        ret->index = index;
        ret -> total = total;
        ret->length = length;

        if(payload)
        {
            memcpy(ret + 1, payload, length);
        }
    }
    return ret;
}

//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_

//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;

static void InitState(MsgParser* p)
{
    p->header = 0;
    p->need = sizeof(p->cache);
    
    free(p->msg);
    
    p->msg = NULL;
}

static int ToMidState(MsgParser* p)
{
	p->header = 1;
    p->need = p->cache.length;
    
    p->msg = malloc(sizeof(p->cache) + p->need);
    if( p->msg )
    {
        *p->msg = p->cache;
    }
    return !!p->msg;
}

static Message* ToLastState(MsgParser* p)
{
    Message* ret = NULL;
    
    if( p->header && !p->need )
    {
        ret = p->msg;
        p->msg = NULL;
    }
    return ret;
}

static void ntoh(Message* m)
{
    m->type = ntohs(m->type);
    m->cmd = ntohs(m->cmd);
    m->index = ntohs(m->index);
    m->total = ntohs(m->total);
    m->length = ntohl(m->length);
}

static int ToRecv(int fd, char* buf, int size)
{
    int retry = 0;
    int i = 0;
    
    while( i < size )
    {
        int len = read(fd, buf + i, size - i);
        if( len > 0 )
        {
            i += len;
        }
        else if( len < 0 )
        {
            break;
        }
        else
        {
            if( retry++ > 5 )
            {
                break;
            }
            usleep(200 * 1000);
        }
    }
    return i;
}

MParser* MParser_New()
{
    MParser* ret = calloc(1, sizeof(MsgParser));
    if( ret )
    {
        InitState(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,解析器状态:还没解析
        {
            int len = (p->need < length) ? p->need : length;
            int offset = sizeof(p->cache) - p->need;
            
            memcpy((char*)&p->cache + offset, mem, len);
            if( p->need == len)
            {
                ntoh(&p->cache);

                mem += p->need;
                length -= p->need;

                if(ToMidState(p))
                {
                    ret = MParser_ReadMem(p, mem, length);//递归调用
                }
                else
                {
                    InitState(p);
                }

            }
            else
            {
                p->need -= len;
            }
        }
        else
        {
            if( p->msg )
            {
                int len = (p->need < length) ? p->need : length;
                int offset = p->msg->length - p->need;

                memcpy(p->msg->payload + offset, mem, len);

                p->need -= len;
            }

            if( ret = ToLastState(p) )
            {
                InitState(p);
            }
        }
    }
    return ret;
}

Message* MParser_ReadFd(MParser* parser, int fd)
{
    Message* ret = NULL;
    MsgParser* p = (MsgParser*)parser;

    if( (fd != -1)&& p )
    {
        if( !p->header )
        {
            //解析消息头
            int offset = sizeof(p->cache) - p->need;
            int len = ToRecv(fd, (char *)&p->cache + offset, p->need);

            if( len == p->need )
            {
                //状态切换
                ntoh(&p->cache);

                if( ToMidState(p) )
                {
                    ret = MParser_ReadFd(p, fd);
                }
                else
                {
                    //无法进入中间态,重置
                    InitState(p);
                }
            }
            else
            {
                p->need -= len;
            }
        }
        else
        {
            //解析payload
            if( p->msg )
            {
                int offset = p->msg->length - p->need;
                int len = ToRecv(fd, p->msg->payload + offset, p->need);
                p->need -= len;
            }

            if( ret = ToLastState(p) )
            {
                InitState(p);
            }
        }
    }
    return ret;
}

void MParser_Reset(MParser* parser)
{
    MsgParser* p = (MsgParser* )parser;
    if( p )
    {
        InitState(p);
    }
}

void MParser_Del(MParser* parser)
{
    MsgParser* p = (MsgParser* )parser;
    if( p )
    {
        free(p->msg);
        free(p);
    }
}

//server.c

#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include "msg_parser.h"

int main()
{
   
    int server = 0;
    struct sockaddr_in saddr = {0};
    int client = 0;
    struct sockaddr_in caddr = {0};
    socklen_t asize = 0;
    int len =0;
    char buf[32] = {0};
    int r = 0;
    MParser* parser = MParser_New();


    server = socket(PF_INET,SOCK_STREAM,0);

    if( server == -1)
    {
        printf("server socket err\n");
        return -1;
    }

    saddr.sin_family = AF_INET;
    saddr.sin_addr.s_addr = htonl(INADDR_ANY);
    saddr.sin_port = htons(20108);

    if( bind(server,(struct sockaddr*)&saddr, sizeof(saddr)) == -1 )
    {
        printf("server bind err\n");
        return -1;
    }

    if( listen(server,1) == -1 )
    {
        printf("listen err\n");
        return -1;
    }
    
    printf("server start success\n");

    while(1)
    {
        struct tcp_info info = {0};
        int l =sizeof(info);
        asize = sizeof(caddr);

        client = accept(server,(struct sockaddr*)&caddr, &asize);
        if(client == -1)
        {
            printf("client accept err\n");
            return -1;
        }

        printf("client : %d\n", client);

        do
        {
            getsockopt(client, IPPROTO_TCP, TCP_INFO, &info, (socklen_t*)&l);
            Message* m = MParser_ReadFd(parser, client);
            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);
*/                printf("m->payload = %s\n",m->payload);
                
                free(m);
             
            }

        } while ( info.tcpi_state == TCP_ESTABLISHED );

        printf("i'll close client...\n");

        close(client);
    }
    close(server);
    printf("i'm closing server...\n");
    return 0;
}
//client.c
#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<stdio.h>
#include<unistd.h>
#include<string.h>
#include<malloc.h>
#include<string.h>
#include"message.h"

static void hton(Message* m)
{
    m->type = htons(m->type);
    m->cmd = htons(m->cmd);
    m->index = htons(m->index);
    m->total = htons(m->total);
    m->length = htonl(m->length);
}

int main()
{
    int sock = 0;
    struct sockaddr_in addr = {0};  
    int len = 0;
    int r = 0;
    int i = 0;
    char* test = "hello world!";
    Message* pm = NULL;

    sock = socket(PF_INET,SOCK_STREAM,0);
    if(sock == -1)
    {
        printf("socket err\n");
        return -1;
    }

    addr.sin_family = AF_INET;
    addr.sin_addr.s_addr = inet_addr("127.0.0.1");
    addr.sin_port = htons(20108);

    if(connect(sock,(struct sockaddr*)&addr,sizeof(addr)) == -1)
    {
        printf("connect err\n");
        return -1;
    }

    printf("connect success\n");

    for(i = 0; i < strlen(test); i++)
    {
        char buff[2] = {0};

        buff[0] = test[i];

        pm = Message_New(128, 129, i, strlen(test), buff, 2);

        hton(pm);

        send(sock, pm, sizeof(Message) + 2, 0);

        free(pm);
    }

    close(sock);

    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值