前文
今天学习了唐老师课程中关于分包黏包问题的解决方案,个人感觉不是很好理解,自己尝试实现并验证了可行性,本着分享的目的在这里介绍一下。
首先叠个甲,个人水平有限,且目前刚接触网络编程相关,这里只是给出我当下想出的一个实现思路,如果发现了问题或者优化方案欢迎评论ovo
什么是分包,黏包?
是在网络通信中由于各种因素(网络环境、API规则等)造成的消息与消息之间出现的两种状态。
分包:一个消息分成多个消息发送。
黏包:多个消息合并成一个消息发送。
如何解决分包黏包?
前置信息:对于一个消息msg,为了区分它属于哪个自定义类,我们在其前面加上一个int标识(4位byte),也就是人为给每个自定义类规定一个id,消息的格式为:消息体的id+消息的内容(消息体)。
利用这个思路,我们可以在此基础上加上一个消息体的长度len,同样为int类型(4位byte),每次解析消息时通过解出来的id和len确定消息体的类型和长度,从而避免分包黏包造成的无法解析出正确的msg的情况,消息的格式为:消息体的id+消息体的长度len+消息的内容(消息体)。
实现思路
这里只给出核心代码及思路,也就是如何对收到的消息的处理因为我懒
代码实现
private byte[] cache = new byte[1024 * 1024];
private int begin = 0, end = 0;
private int id = -1, len = -1;
private void HandleReceiveMsg(byte[] bytes, int receiveNum)
{
//将byte放入cache
bytes.CopyTo(cache, end);
end += receiveNum;
//不断解析直到无法操作,保证离开时cache都是无法直接处理的状态
while (true)
{
//得到id,len
if (id == -1 && len == -1 && end - begin < 8) return;
if (id == -1 && len == -1)
{
id = BitConverter.ToInt32(cache, begin);
begin += 4;
len = BitConverter.ToInt32(cache, begin);
begin += 4;
}
//判断能否处理
// 否,直接退出
if (end - begin < len) return;
// 能,解析出一条数据(处理完记得将id,len归为-1)
BaseMsg msg = null;
switch (id)
{
case 1001:
PlayerMsg playerMsg = new PlayerMsg();
playerMsg.Reading(cache, begin);
msg = playerMsg;
break;
default:
break;
}
if(msg!=null)
receiveQueue.Enqueue(msg);
begin += len;
id = -1;
len = -1;
//解析完一次后,将处理完的内容从缓存中弹出
Array.Copy(cache,begin,cache,0,end-begin);
end -= begin;
begin = 0;
}
}