主要是解TCP字节流的问题。
CMPP协议,中国移动通信互联网短信网关接口协议(China Mobile Peer to Peer, CMPP),其消息结构,由消息头(所有消息公共包头)Message Header和消息体Message Body两部分组成。
其中,Message Header由3个字段组成:
字段名 | 字节数 | 类型 | 描述 |
---|---|---|---|
Total_Length | 4 | Unsigned Integer | 消息总长度(含消息头及消息体) |
Command_Id | 4 | Unsigned Integer | 命令或响应类型 |
Sequence_Id | 4 | Unsigned Integer | 消息流水号,顺序累加,步长为1,循环使用(一对请求和应答消息的流水号必须相同) |
在libevent的read callback函数中,
我是先获取当前读缓冲区中字节数len是多少,如果大于等于12,即收齐了Message Header,
再调用peekInt32()看Total_Length是多少,如果len大于等于Total_Length,则代表一个完成的Message已经收齐,
然后调用peekInt32()看Command_Id是多少,再根据Command_Id进行解包。
![](https://i-blog.csdnimg.cn/blog_migrate/852a1bf8257cc4d180c258bea42de3fc.png#pic_center)
peekInt32()是我自己写的,调用了evbuffer_peek(),仅用于查看缓存区数据,不对缓冲区做其他修改操作。问题就出在这个函数。
![](https://i-blog.csdnimg.cn/blog_migrate/f829bac73520e08b39c3ab6a2f698a85.png#pic_center)
evbuffer中的字节存储在多个单独的大块内存中,从源码可以看出,evbuffer_peek()其实就是将这些单独的内存块地址的指针和大小,填充到evbuffer_iovec结构体中。
而evbuffer_iovec结构体,就是内存块起始指针,和内存块的大小。
所以,peekInt32()的问题就在于,只给了1个evbuffer_iovec,
如果evbuffer_peek()填充的第一块内存,小于8字节,比如只有7个字节,那么问题就来了。
Total_Length读取的是前4个字节(0~3),是可以正确地读取到值的。
而Command_Id读取后4个字节(4~7),但此时只剩下4~6可读取了,读取的字节不够,Command_Id自然就不正确了。
![](https://i-blog.csdnimg.cn/blog_migrate/55ccc214c190ab7b46f6627ec5987092.png#pic_center)
evbuffer_peek()的返回值,代表所需要的evbuffer_iovec元素的数量,而且还需要手动处理两个不连续的内存块。对于我们这种简单的读取2个uint32略显复杂了,很明显不适用这个场景。所以,我换成了使用evbuffer_copyout(),一次性拷贝8个字节,再读入到两个uint32中。