基于DragonBoard 410c的遥控炮台七之控制协议优化(中)

一.背景

本章博主接上一节内容,给大家分享下如何解决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 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);
 }


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值