基于DragonBoard 410c的遥控炮台七之控制协议优化(中)
2017-05-03 11:51
423 查看
一.背景
本章博主接上一节内容,给大家分享下如何解决Socket通信的分包现象导致遥控信号的不稳定的解决方法。
二.Socket自定义协议
1.应用场景
回顾下Socket的分包与粘包场景:
(1)完整的一条消息被系统拆分成几条发送,例如要发送一条消息:Hello World ,却被系统分成两条
消息发送,分别为:Hello 和 World。
(2)几条独立的消息被系统合成一条消息发送,例如要发送两条消息分别为:a memory from my past和
it’s been a year,却被系统和成一条消息发送:a memory from my pastit’s been a year。
解决这个分包问题的关键,就是为socket通讯定制一套协议,以保证数据在分包与粘包场景下的接收端的判断完整性。
2.自定义协议:
(1).协议数据格式:
Head:帧头,2个字节,此处为0xa001
Type:通信类型,1个字节,(这里把范围设置在0x00~0xff)
Data Length:数据长度,1个字节,即Data的字节总数
Data:实际传输的数据,长度不定
CS:校验值,1个字节,(传统是type、data length、data三个域所有字节的异或值,这里实际不使用)
End:帧尾,2个字节,此处为0x100a
(2).协议解析策略:
我们假设:手机端=客户端C,炮台从机端=服务端S,那么当socket客户端C和服务端S通信,C向S发送消息M1:
A.S收到消息M1。S把消息M1拷贝到缓存Q中,Q为循环队列。假如M1的长度大于Q的剩余空间,则只拷贝剩余空间大小的字节到Q。
B.从Q的当前指针开始,查找帧头<Head>。如果找到,则当前指针向后移2个字节位置,继续查找<Type>;如果没找到,则删除前1个字节,当前指针向后移1个字节位置,继续查找<Head>
C.从Q的当前指针开始,查找<Type>。如果Q中至少还剩一个字节,则表示找到,当前指针向后移1个字节位置,否则退出解析。
D.从Q的当前指针开始,查找<DataLength>。如果Q中至少还剩一个字节,则表示找到,当前指针向后移1个字节位置,否则退出解析。
E.从Q的当前指针开始,向后移DataLength个字节位置,查找<End>。如果找到,则从Q中取出一条完整的消息P1,并从Q中删除此消息空间,调用外部的回调函数;否则删除帧头的第一个字节a5,当前指针指向帧头第二个字节a5位置,从步骤2开始,重新一轮解析。
三.关键代码
1.数据结构:
typedef enum{
SEARCH_HEAD,
SEARCH_TYPE,
SEARCH_LEN,
//SEARCH_CS,
SEARCH_END,
SEARCH_NONE
}cache_strategy;
typedef struct{
unsigned char data[SOCKET_MSG_SIZE]; //data
int len;
unsigned char type;
}socket_msg;
2.关键实现:
本章博主接上一节内容,给大家分享下如何解决Socket通信的分包现象导致遥控信号的不稳定的解决方法。
二.Socket自定义协议
1.应用场景
回顾下Socket的分包与粘包场景:
(1)完整的一条消息被系统拆分成几条发送,例如要发送一条消息:Hello World ,却被系统分成两条
消息发送,分别为:Hello 和 World。
(2)几条独立的消息被系统合成一条消息发送,例如要发送两条消息分别为:a memory from my past和
it’s been a year,却被系统和成一条消息发送:a memory from my pastit’s been a year。
解决这个分包问题的关键,就是为socket通讯定制一套协议,以保证数据在分包与粘包场景下的接收端的判断完整性。
2.自定义协议:
(1).协议数据格式:
Head:帧头,2个字节,此处为0xa001
Type:通信类型,1个字节,(这里把范围设置在0x00~0xff)
Data Length:数据长度,1个字节,即Data的字节总数
Data:实际传输的数据,长度不定
CS:校验值,1个字节,(传统是type、data length、data三个域所有字节的异或值,这里实际不使用)
End:帧尾,2个字节,此处为0x100a
(2).协议解析策略:
我们假设:手机端=客户端C,炮台从机端=服务端S,那么当socket客户端C和服务端S通信,C向S发送消息M1:
A.S收到消息M1。S把消息M1拷贝到缓存Q中,Q为循环队列。假如M1的长度大于Q的剩余空间,则只拷贝剩余空间大小的字节到Q。
B.从Q的当前指针开始,查找帧头<Head>。如果找到,则当前指针向后移2个字节位置,继续查找<Type>;如果没找到,则删除前1个字节,当前指针向后移1个字节位置,继续查找<Head>
C.从Q的当前指针开始,查找<Type>。如果Q中至少还剩一个字节,则表示找到,当前指针向后移1个字节位置,否则退出解析。
D.从Q的当前指针开始,查找<DataLength>。如果Q中至少还剩一个字节,则表示找到,当前指针向后移1个字节位置,否则退出解析。
E.从Q的当前指针开始,向后移DataLength个字节位置,查找<End>。如果找到,则从Q中取出一条完整的消息P1,并从Q中删除此消息空间,调用外部的回调函数;否则删除帧头的第一个字节a5,当前指针指向帧头第二个字节a5位置,从步骤2开始,重新一轮解析。
三.关键代码
1.数据结构:
typedef enum{
SEARCH_HEAD,
SEARCH_TYPE,
SEARCH_LEN,
//SEARCH_CS,
SEARCH_END,
SEARCH_NONE
}cache_strategy;
typedef struct{
unsigned char data[SOCKET_MSG_SIZE]; //data
int len;
unsigned char type;
}socket_msg;
2.关键实现:
/parsed the packaged data, and invoke callback function void socket_msg_parse(int fd, socket_cache *cache) { int current_len; int p, q; int i; int find; if(cache->front == cache->rear && cache->tag == 0){ //D("socket cache is empty!\n"); return; } //calculate the current length of cache if(cache->current >= cache->front){ current_len = cache->len - (cache->current - cache->front); } else{ current_len = cache->rear - cache->current; } switch(cache->strategy){ case SEARCH_HEAD://to find a Head format in cache if(current_len < SOCKET_MSG_HEAD_SIZE){ return; } find = FALSE; for(i = 0; i < current_len - 1; i++){ p = cache->current; q = (cache->current + 1) % SOCKET_MSG_CACHE_SIZE; if( (cache->buf[p] == (SOCKET_MSG_HEAD >> 8))&& (cache->buf[q] == (SOCKET_MSG_HEAD & 0xff))){ find = TRUE; break; //exit for loop } else{ //current pointer move to next cache->current = q; //delete one item cache->front = cache->current; cache->len --; cache->tag = 0; } } if(find == TRUE){ //move 2 items towards next cache->current = (cache->current + 2) % SOCKET_MSG_CACHE_SIZE; //we found the head format, go on to find Type byte cache->strategy = SEARCH_TYPE; } else{ //if there is no head format ,delete previouse items LOGE("socket message without head: %x!\n",SOCKET_MSG_HEAD); //go on to find Head format cache->strategy = SEARCH_HEAD; } break; case SEARCH_TYPE://to find the type byte in cache if(current_len < SOCKET_MSG_TYPE_SIZE){ return ; } //get the value of type //cache->type = cache->buf[cache->current]; cache->recv_msg.type = cache->buf[cache->current]; cache->current = (cache->current + 1) % SOCKET_MSG_CACHE_SIZE; //we found Type byte, go on to find Datalen format cache->strategy = SEARCH_LEN; break; case SEARCH_LEN://to find the datalen 4000 byte in cache if(current_len < SOCKET_MSG_LEN_SIZE){ return ; } if(cache->buf[cache->current] > SOCKET_MSG_DATA_SIZE){ LOGE("the data len of message out of size: %d!\n",SOCKET_MSG_DATA_SIZE); //delete the frist item 'a5' //move back 2 items cache->current = cache->current >= 2 ? (cache->current - 2) : (SOCKET_MSG_CACHE_SIZE - 2 + cache->current); cache->front = cache->current; //length sub 2 cache->len -= 2; cache->tag = 0; //go on to find Head format cache->strategy = SEARCH_HEAD; } else{ //get the value of datalen //cache->data_len = cache->buf[cache->current]; cache->recv_msg.len = cache->buf[cache->current]; cache->current = (cache->current + 1) % SOCKET_MSG_CACHE_SIZE; //we found datalen byte, go on to find End format cache->strategy = SEARCH_END; } break; case SEARCH_END: if(current_len < (cache->recv_msg.len + SOCKET_MSG_END_SIZE)){ return; } //because we have known the data bytes' len, so we move the very //distance of datalen to see if there is End format. p = (cache->current + cache->recv_msg.len) % SOCKET_MSG_CACHE_SIZE; q = (cache->current + cache->recv_msg.len + 1) % SOCKET_MSG_CACHE_SIZE; if( (cache->buf[p] == (SOCKET_MSG_END >> 8))&& (cache->buf[q] == (SOCKET_MSG_END & 0xff)) ){ socket_msg_cpy_out(cache, cache->recv_msg.data, cache->current, cache->recv_msg.len); if(cache->handle != NULL){ //cache->handle(fd, cache->buf + cache->data_index, cache->data_len); cache->handle(fd, &cache->recv_msg, cache->args); } //delete all previous items cache->current = (q + 1) % SOCKET_MSG_CACHE_SIZE; cache->front = cache->current; cache->len -= (cache->recv_msg.len + SOCKET_MSG_FORMAT_SIZE); cache->tag =0; } else{ LOGE("socket message without end: %x!\n",SOCKET_MSG_END); //delete the frist item 'a5' //move back 3 items cache->current = cache->current >= 3 ? (cache->current - 3) : (SOCKET_MSG_CACHE_SIZE - 3 + cache->current); cache->front = cache->current; //length sub 3 cache->len -= 3; cache->tag = 0; } //go on to find Head format cache->strategy = SEARCH_HEAD; break; default: break; } //parse new socket message socket_msg_parse(fd,cache); }
相关文章推荐
- 基于DragonBoard 410c的遥控炮台八之控制协议优化(下)
- 基于DragonBoard 410c的遥控炮台六之控制协议优化(上)
- 基于DragonBoard 410c的遥控炮台三之远程交互(上)
- 基于DragonBoard 410c的遥控炮台二之舵机驱动(下)
- 基于DragonBoard 410c的遥控炮台五之远程交互(下)
- 基于dragonboard 410c的kobuki机器人简单控制——驱动安装
- 基于Dragonboard 410c的总线控制之SPI(一)
- 基于DragonBoard 410c的家庭智能环保卫士——(13)融合多数据处理的运动控制
- 如何实现DragonBoard 410c GPIO控制(基于linux环境)
- 基于Dragonboard 410c的总线控制之I2C(三)
- 基于Dragonboard 410c的总线控制之UART(二)
- 基于Dragonboard 410c的总线控制之SPI(二)
- 基于Dragonboard 410c的总线控制之UART(一)
- 基于Dragonboard 410c的总线控制之I2C(一)
- 基于dragonboard 410c的kobuki机器人控制——远程控制
- 基于Dragonboard 410c的总线控制之I2C(二)
- 基于dragonboard 410c的智能魔镜设计(1)——整体方案设计
- DragonBoard 410c手把手控制Linux的GPIO
- 基于dragonboard 410c的智能魔镜设计(5)——基于人脸的用户身份识别
- 基于DragonBoard 410c的远程家居监控平台一之远程图像传输(上)