安卓Socket拆包问题

安卓拆包导致问题

1.socket读取到ByteArray前面部分正确,后面剩余部分全部为0;
2.由于socket读取长度不正确,进而出现连锁反应,导致后面获取到的包头不正确,于是出现以下闪退信息。

java.lang.OutOfMemoryError: Failed to allocate a 2021226312 byte 
allocation with 9337485 free bytes and 183MB until OOM, 
target footprint 18674973, growth limit 201326592

分析

socket服务器协议大致如下:

  • 1.固定包头长度11byte,其中包含包体长度信息;
  • 2.根据包头信息读取包体。

于是socket读取消息代码如下:

val inputStream = clientSocket!!.getInputStream()
val message = HlllSocketMessage() //自定义消息实体
var buffer = ByteArray(headerLength) //读取二进制数据
var len = -1
while (clientSocket!!.isConnected && inputStream.read(buffer).also { len = it } != -1) {
    if (len == headerLength) {
        message.parseHeader(buffer) //解析包头信息
        buffer = ByteArray(message.bodyBytesLength)
    } else {
        message.parseBodyData(buffer) //解析包体信息
        buffer = ByteArray(headerLength)
        onReceivedMessage(message)
     }
}

这么写,在一般情况下没什么问题,但是在高并发时就出现上边所说的问题。

你会发现实际上读取到的长度len和所需要读取的长度message.bodyBytesLength不一致!

原因是inputStream读取长度有限制,所以自动拆包了!

inputStream.available()方法能够获取到剩余可用长度,但是inputStream.read(buffer)也能够获取到当前实际获取到的信息长度,用后者即可。

解决方案

val inputStream = clientSocket!!.getInputStream();
val message = HlllSocketMessage();
var buffer = ByteArray(headerLength);
var readLen = headerLength; //实际读取长度
var len = headerLength; //需要读取的长度
var leftLen = headerLength; //剩余未读长度
while (clientSocket!!.isConnected && readLen != -1) {
    synchronized(buffer) Runnable@ {
        val offset = len - leftLen;
        readLen = inputStream.read(buffer, offset, leftLen); //读取长度
        if (readLen == -1) {
            return@Runnable;
        }

        if (readLen < leftLen) { //处理拆包
            leftLen = leftLen - readLen;
        } else {
            leftLen = 0;
        }

        if (leftLen == 0) { //需要读取长度跟实际长度一致才处理
            if (len == headerLength) { //包头
                message.parseHeader(buffer);
                len = message.bodyBytesLength;
                leftLen = len;
                buffer = ByteArray(len);
            } else { //包体
                message.parseBodyData(buffer);
                len = headerLength;
                leftLen = len;
                buffer = ByteArray(len);
                onReceivedMessage(message);
            }
        }
    }
}

你可能感兴趣的:(安卓Socket拆包问题)