「这是我参与2022首次更文挑战的第19天,活动详情查看:2022首次更文挑战」
Socket TCP一种合包思路的分享
上篇文章讲过TCP是流的形式,并不是发送什么就能收到什么,而是要在应用层去做逻辑处理,才能接收到正确的数据。
合包的解决方法
回顾一下合包的解决方法。
-
发送时数据末尾拼接换行符,使用readLine方法接收(只能接收字符串)
-
约定分割符,每个字节进行判断(效率低)
-
使用webSocket基于包协议的技术(局限)
-
约定每一包的长度,每一包前加上包的长度,比如[5][12345]这样的结构
这篇文章是为了详细讲解方法四的思路。
可变数组长度
在讲解之前先了解可变数组长度怎么实现,字节数据不同于字符串,没有很方便的StringBuffer、StringBuilder方法,那么怎么实现可变长度的数组,方便起见,可以直接使用ByteArrayOutputStream
ByteArrayOutputStream如何创建使用,有两种方法可以创建它。
- 不指定大小的方式,默认开辟空间是32字节
OutputStream bOut = new ByteArrayOutputStream();
- 指定大小的方式,比如开辟大小为a的空间
OutputStream bOut = new ByteArrayOutputStream(int a)
它的大小空间是可以变的,不像新创建字节数组时,必须要指定长度。在添加字节数组时,使用write方法就可以写入了,转成字节数组时,再使用toByteArray方法。
思路分析
在发送端,我们给每包发送的数据增加一个整形的长度。比如
原来字节数组是: [1,2,3]
加上整形长度后是: 整型3转字节数组(4位) 拼接上 [1,2,3]
由于TCP是流,在大量数据传输时,并不能一次性的接收全。在之前的文章中讲过,不再细说。
在接收端,就需要每次判断内容是否接收全,提取真实包的长度,进而提取出真实的数据包。
笔者用递归写了一个简单的处理,含义是,先将接收的数据包保存到可变数组中,然后判断是否超过4个字节,超过4个字节就提取约定的包的长度,没有超过就继续进行接收。
可变数组的长度 > 约定的包长度+4 时,开始提取真实的包数据。
看代码吧,以后看了开源框架之后,再优化这部分代码。
注意在每次链接成功时,重置 可变数组和包长度的变量。
/*可变数组*/
ByteArrayOutputStream stream = new ByteArrayOutputStream();
/*包长度的约定*/
int frameLen = 0;
private synchronized void handleReceiveTcpByte(byte[] buffer) throws IOException {
stream.write(buffer);
deal();
}
private void deal() throws IOException {
if (frameLen == 0) {
if (stream.size() > 4) {
// 取出实际包的长度
byte[] tempArray = stream.toByteArray();
byte[] intLengthArray = new byte[4];
System.arraycopy(tempArray, 0, intLengthArray, 0, 4);
frameLen = ByteUtils.bytes2IntLittle(intLengthArray);
// 进行递归
deal();
} else {
// 无需操作,等待下一次的包
}
} else {
if (stream.size() >= frameLen + 4) {
byte[] tempArray = stream.toByteArray();
byte[] framePacket = new byte[frameLen];
System.arraycopy(tempArray, 4, framePacket, 0, frameLen);
if (mTcpSocketCallback != null)
mTcpSocketCallback.onReceived(framePacket);
// 取出之后,剩下的空间继续放入 stream
byte[] remainPacket = new byte[stream.size() - frameLen + 4];
System.arraycopy(tempArray,frameLen + 4 , remainPacket, 0, stream.size() - frameLen + 4);
stream.reset();
stream.write(remainPacket);
} else {
// 无需操作,等待下一次的包
}
}
}