基于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通信设计一种通信协议,以保证数据的准确性。

协议格式

通信协议设计如下:


 

Head:帧头,2个字节,此处为0xa5a5

Type:通信类型,1个字节,范围0x00~0xff    

Data Length:数据长度,1个字节,即Data的字节总数,

Data:实际传输的数据,长度不定

CS:校验值,1个字节,type、data length、data三个域所有字节的异或值,实际中并没用到校验

End:帧尾,2个字节,此处为0xbeef

 

3 程序设计

 

3.1    解析思路

假设socket客户端C和服务端S通信,C向S发送消息M1。

1、  S收到消息M1。S把消息M1拷贝到缓存Q中,Q为循环队列。假如M1的长度大于Q的剩余空间,则只拷贝剩余空间大小的字节到Q。

2、  从Q的当前指针开始,查找帧头

。如果找到,则当前指针向后移2个字节位置,继续查找;如果没找到,则删除前1个字节,当前指针向后移1个字节位置,继续查找

3、  从Q的当前指针开始,查找。如果Q中至少还剩一个字节,则表示找到,当前指针向后移1个字节位置,否则退出解析。

4、  从Q的当前指针开始,查找。如果Q中至少还剩一个字节,则表示找到,当前指针向后移1个字节位置,否则退出解析。

5、  从Q的当前指针开始,向后移DataLength个字节位置,查找。如果找到,则从Q中取出一条完整的消息P1,并从Q中删除此消息空间,调用外部的回调函数;否则删除帧头的第一个字节a5,当前指针指向帧头第二个字节a5位置,从步骤2开始,重新一轮解析。

 

3.2    数据结构

查找策略枚举,用于查找时判断查找帧结构的哪个部位:

复制代码
1 typedef enum{
2     SEARCH_HEAD,
3     SEARCH_TYPE,
4     SEARCH_LEN,
5     //SEARCH_CS,
6     SEARCH_END,
7     SEARCH_NONE
8 }cache_strategy;
复制代码

消息结构体,用于存储从缓存中解析出的数据:

复制代码
typedef struct{
    unsigned char data[SOCKET_MSG_SIZE];            //data
    int len;
    unsigned char type;
}socket_msg;
复制代码

回调函数,用于从缓存中解析出消息时调用:

typedef void (*tp_socket_msg_handle)(int fd, socket_msg *msg,void *args);  

循环队列,用于缓存接收到的数据:

复制代码
typedef struct{
    unsigned char buf[SOCKET_MSG_CACHE_SIZE]; //buffer for storing data read from client
    int front;
    int rear;
    int current;    
    int len;
    int tag;            //mark that whether the cache is full,1-full,0-not full
    cache_strategy strategy;
    tp_socket_msg_handle handle;//callback function to invoke when a message is parsed out        
    void* args;                                    //external parameter
    socket_msg recv_msg;                            //parsed message
    
}socket_cache;
复制代码

3.3 关键实现

 1、把接收到的数据存储到缓冲中,并准备解析:

复制代码
//copy the unparsed data to cache, and parsed them
int socket_msg_pre_parse(
int fd, 
socket_cache *cache,
unsigned char *buf, 
int len, 
void *args)
{
    int n = 0;
    unsigned char *p = buf;
    //when reading buffer's length is greater than cache's left length,
    //we should copy many times.
    cache->args = args;
    while(1){
        n = socket_msg_cpy_in(cache, p, len);
        if(n == 0){
            return FALSE;//cache is full    
        }
        //parse and handle socket message from cache
        socket_msg_parse(fd, cache);
        
        if(n == len){
            return TRUE; //copy completed
        }
        //move the pointer
        p     = p + n;
        len = len - n;
    }
    
    return TRUE;
        
}
复制代码

2、递归解析消息:

复制代码
  1 //parsed the packaged data, and invoke callback function
  2 void socket_msg_parse(int fd, socket_cache *cache)
  3 {
  4     int current_len;
  5     int p, q;
  6     int i;
  7     int find;
  8     
  9     if(cache->front == cache->rear && cache->tag == 0){
 10         //D("socket cache is empty!\n");
 11         return;    
 12     }
 13     
 14     //calculate the current length of cache
 15     if(cache->current >= cache->front){
 16         current_len = cache->len - (cache->current - cache->front);
 17     }
 18     else{
 19         current_len = cache->rear - cache->current;    
 20     }
 21     
 22     switch(cache->strategy){
 23         case SEARCH_HEAD://to find a Head format in cache
 24             if(current_len < SOCKET_MSG_HEAD_SIZE){
 25                 return;    
 26             }
 27             find = FALSE;
 28             for(i = 0; i < current_len - 1; i++){
 29                 p = cache->current;
 30                 q = (cache->current + 1) % SOCKET_MSG_CACHE_SIZE;
 31                 if(    (cache->buf[p] == (SOCKET_MSG_HEAD >> 8))&&
 32                     (cache->buf[q] == (SOCKET_MSG_HEAD & 0xff))){
 33                     
 34                     find = TRUE;
 35                     break; //exit for loop
 36                 }
 37                 else{
 38                     //current pointer move to next
 39                     cache->current = q;
 40                     //delete one item
 41                     cache->front = cache->current;
 42                     cache->len --;
 43                     cache->tag = 0;
 44                 }
 45             }
 46             
 47             if(find == TRUE){    
 48                 //move 2 items towards next
 49                 cache->current = (cache->current + 2) % SOCKET_MSG_CACHE_SIZE;    
 50                 //we found the head format, go on to find Type byte
 51                 cache->strategy = SEARCH_TYPE;        
 52             }
 53             else{
 54                 //if there is no head format ,delete previouse items
 55                 LOGE("socket message without head: %x!\n",SOCKET_MSG_HEAD);
 56                 //go on to find Head format
 57                 cache->strategy = SEARCH_HEAD;    
 58             }
 59             break;
 60             
 61         case SEARCH_TYPE://to find the type byte in cache
 62             if(current_len < SOCKET_MSG_TYPE_SIZE){
 63                 return ;    
 64             }
 65             //get the value of type
 66             //cache->type = cache->buf[cache->current];
 67             cache->recv_msg.type = cache->buf[cache->current];
 68             cache->current = (cache->current + 1) % SOCKET_MSG_CACHE_SIZE;
 69             //we found Type byte, go on to find Datalen format
 70             cache->strategy = SEARCH_LEN;
 71             break;
 72             
 73         case SEARCH_LEN://to find the datalen byte in cache
 74             if(current_len < SOCKET_MSG_LEN_SIZE){
 75                 return ;    
 76             }
 77             if(cache->buf[cache->current] > SOCKET_MSG_DATA_SIZE){
 78                 LOGE("the data len of message out of size: %d!\n",SOCKET_MSG_DATA_SIZE);
 79                 //delete the frist item 'a5'
 80                 //move back 2 items
 81                 cache->current = cache->current >= 2 ? (cache->current - 2) : (SOCKET_MSG_CACHE_SIZE - 2 + cache->current);
 82                 cache->front = cache->current;
 83                 //length sub 2
 84                 cache->len -= 2;
 85                 cache->tag = 0;
 86                 //go on to find Head format
 87                 cache->strategy = SEARCH_HEAD;
 88             }
 89             else{
 90                 //get the value of datalen
 91                 //cache->data_len = cache->buf[cache->current];
 92                 cache->recv_msg.len = cache->buf[cache->current];
 93                 cache->current = (cache->current + 1) % SOCKET_MSG_CACHE_SIZE;
 94                 //we found datalen byte, go on to find End format
 95                 cache->strategy = SEARCH_END;
 96             }
 97             break;
 98             
 99         
100             
101         case SEARCH_END:
102             if(current_len < (cache->recv_msg.len + SOCKET_MSG_END_SIZE)){
103                 return;    
104             }
105             //because we have known the data bytes' len, so we move the very  
106             //distance of datalen to see if there is End format. 
107             p = (cache->current + cache->recv_msg.len) % SOCKET_MSG_CACHE_SIZE; 
108             q = (cache->current + cache->recv_msg.len + 1) % SOCKET_MSG_CACHE_SIZE; 
109             if(    (cache->buf[p] == (SOCKET_MSG_END >> 8))&&
110                 (cache->buf[q] == (SOCKET_MSG_END & 0xff)) ){
111                 socket_msg_cpy_out(cache, cache->recv_msg.data, cache->current, cache->recv_msg.len);
112                 if(cache->handle != NULL){
113                     //cache->handle(fd, cache->buf + cache->data_index, cache->data_len);
114                     cache->handle(fd, &cache->recv_msg, cache->args);    
115                 }
116                 //delete all previous items
117                 cache->current = (q + 1) % SOCKET_MSG_CACHE_SIZE;
118                 cache->front = cache->current;
119                 cache->len -= (cache->recv_msg.len + SOCKET_MSG_FORMAT_SIZE);
120                 cache->tag =0;
121                 
122             }
123             else{
124                 LOGE("socket message without end: %x!\n",SOCKET_MSG_END);
125                 //delete the frist item 'a5'
126                 //move back 3 items
127                 cache->current = cache->current >= 3 ? (cache->current - 3) : (SOCKET_MSG_CACHE_SIZE - 3 + cache->current);
128                 cache->front = cache->current;
129                 //length sub 3
130                 cache->len -= 3;
131                 cache->tag = 0;
132                     
133             }
134             //go on to find Head format
135             cache->strategy = SEARCH_HEAD;
136             break;
137             
138         default:
139                 break;
140                 
141                 
142     }
143     
144     //parse new socket message
145     socket_msg_parse(fd,cache);
146 }
复制代码

原文:http://blog.csdn.net/xnwyd/article/details/7668742

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值