我翻阅大量书籍资料选择了消息长度包头4字节+消息内容协议接收数据时,包头4字节发生"粘包"现象,4字节长度有时候不在networksstream缓冲区首位,导致数据错乱,请问有什么解决办法,平台是C#,长连接。代码如下:
private void AccepMessage()
{
NetworkStream netStream = new NetworkStream(socket);
while (true)
{
try
{
byte[] datasize = new byte[4];
netStream.Read(datasize, 0, 4);//先读取长度4字节
int size = System.BitConverter.ToInt32(datasize, 0);
Byte[] message = new byte[size];
int dataleft = size;
int start = 0;
while (dataleft > 0)
{
int recv = netStream.Read(message, start, dataleft);//再读数据内容
start += recv;
dataleft -= recv;
}
this.rchTxtBoxReceive.Text = System.Text.Encoding.Unicode.GetString(message);
}
catch
{
}
}
}
冥思苦想数日,感觉碰了墙,请指点迷津。
如果包头长度4字节错位不在缓冲区首位,有什么方法可以移到首位?似乎要加开始符索引?
另外 如果表示包头长度的4字节本身粘包了咋办?
在取包头4字节那,也要判断是否取得了4字节,不够继续获取。和下面取包体一样
如果包头4字节不在流的首位怎么办?
没有什么首位不首位的,要多少个字节给你多少字节,数据流跟水流一样,没有什么首位。你先按我说的试试
我的意思是:length+body,如是length本身粘包了咋办,比如body+length+body,gth+body+len,如果长度头自身不完整,也就不法读到正确的消息。
我们不管他是不是在缓冲区首位
如果不讲啥内存效率的化,简单伪代码是
indexof(span<byte>xxx ,"头部特征")--------查找第一个头部特征所在index
然后所有的操作都是从这个index开始,从这个index 向后提取,到符合尾部
如果查到尾部,删除尾部之前所有的数据,只保留尾部以后的数据。如果没有查到尾部,那就啥都不操作,留着(当然正常简单处理到这就差不多了,如果是复 杂比如怕伪造大数据封包,可以带检查条件,不符合条件的,从index再往后查第一个匹配头部特征的,也就是既然前一个不符合规则,扔掉检查下一个头部)
重复上述动作
我是先接收满4字节,计算出包长度后,再根据长度接收指定长度字节数,不管后面还有没有数据,直接截断,算小蜜蜂论坛发帖机一个包,后面的数据都算下一个包,再接收4个字节,算长度。。。。。。。
4字节长度信息不完整、截断粘包咋办。不够4字节就继续接啊
用以下代码去收4个字节:
var bytes4 = ReadAllBytes(netStream, 4);
// ReadAllBytes你还可以写成一个扩展方法。还可以自己实现异步的ReadAllBytesAsync。
public static byte[] ReadAllBytes(Stream stream, int count)
{
var bytes = new byte[count];
for(var offset = 0; offset<count;)
{
var read = stream.Read(bytes, offset, count - offset);
if (read == 0) throw new IOException("Unexpect end of stream");
offset += read;
}
return bytes;
}
请测试几个问题:
1:TCP短连接会有粘包问题吗?
2:TCP长连接不同连接传的数据会粘包吗?
现在正在接联通SGIP,人家定义的头格式不也类似吗?
4.2.2消息头的格式
字段 长度(字节) 类型 说明
Message Length 4 Integer 消息的总长度(字节)
Command ID 4 Integer 命令ID
Sequence Number 12 Integer 序列号
表4-14消息头的格式