应用协议解析模块(下)
1、问题
如何通过socket文件描述符实时解析协议消息?
2、深度思考
从文件描述符是否能够获取足够的数据?
-
数据量足够
- 读取12字节解析消息头
- 读取数据填充payload(length)
-
数据量不足
- 无法获取消息头所需数据(如何处理?解析状态如何切换?)
- 无法获取payload完整数据(如何处理?是否追加?)
3、解决方案
策略:尽力获取数据,实时解析
- 即便当前获取1字节,也可根据状态进行解析
- 支持不同数据源多次接力解析(从内存或文件描述符交替获取数据)
充分利用解析器状态信息是实现解决方案的关键!!
4、解析器状态切换
- 状态切换函数
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;
}