TCP协议发送的是字节流,前后之间的间隔在哪里是不确定的,所有有可能出现粘包现象。
解决粘包问题主要有三个办法
(1).发送固定长度的包,这样接受方也接受固定长度,很显然这种办法很死板。
(2).指定字符串位为包的结束标志。这种方法有FTP和SMPT协议采用。
(3).使用包头+包体的方法。这种格式的包一般分为两个部分,包头和包体,包头是固定大小的,且包头必须包含一个字段来说明接下来的包体有多大。比如:
struct msg_header{
int32_t bodysize;//指定包头的大小
int32_t cmd;
};
这样包头的大小是固定的,为sizeof(struct msg_header),协议中需要先接受包头的大小,然后解析包头,根据解析出来的信息,来接受后面的包体,这样就组成了一个完整的包来处理。
如果协议是包头+包体的方式(其他两种方式其实也一样)
收到数据后,先接受包头,判断数据是否是包头大小,如果没有,则退出;如果有,则解析包头,计算接下来需要接受包体的大小。接受数据,判断数据是否达到要求,如果没有则退出,如果有则解析包体。
这里面有一些细节问题,比如取出包头不要把包头从缓存区中取出来,而是复制出来,因为如果剩余的空间不足以存放包体大小,我们还得做扩充处理,然后把包体放回去。也就是说,我们每次取出数据的时候,最好是把包体和包头一起取出来,清空缓存。
接受包头,解析包体要放在一个循环体里面!因为包太多,我们没办法同时处理多个包。
如果我们使用struct方法来处理TLV,则会发生格式过于死板的问题,后续版本的更新会很难进行。为了兼容版本,同时节省空间,获得更加灵活的协议,于是出现了TLV(type length value)协议,就是再每一个字段前面加一个类型字段。
struct msg_TLV{
short type1;
int version;
short type2;
char name[10];
};
这样我们可以根据type来判断后面的字段,协议就变得十分灵活
缺点就是多了一个类型字段,占据的空间变大了,还要判断类型,解析后面的信息。