Socket粘包,拆包处理

在iOS中用socket做即时通讯时,经常会遇到粘包的问题。

什么是粘包?

粘包:socket传输数据是由多个连续的数据包组成,他们被连续的存储在缓存中,在读取数据包时可能由于某些原因导致获取到了错误的发送边界,这时后就会出现后一个数据包的头连接在了前一个数据包的尾部。

怎么解决粘包?

因为TCP协议是基于字节流的,所以在应用层就要自己解决数据的边界问题。
一般来说,定义数据有两种方式,定义长度 或者 定义一个终结符。
下面通过项目中的代码来解释怎么解决粘包。

func didReadData(_ data:Data){
        //接收的数据先写入缓存
        cache += [UInt8](data)
        while cache.count>8 {
            print("读取到数据,开始解析,剩余数据长度:\(cache.count)")
            //0-4位存储消息类型
            let typeBytes = cache[0..<4]
            //4-8位存储数据包长度
            let lengthBytes = cache[4..<8]
            let typeData = Data(typeBytes)
            let lengthData = Data(lengthBytes)
            //注意大小端转换问题
            let type = Int32(bigEndian: typeData.withUnsafeBytes { $0.baseAddress!.bindMemory(to: Int32.self, capacity: 4).pointee })
            let length = Int32(bigEndian: lengthData.withUnsafeBytes { $0.baseAddress!.bindMemory(to: Int32.self, capacity: 4).pointee })
            //数据包长度不够,跳出循环,继续读取笑一个包
            if cache.count < 8+Int(length){
                break
            }
            //获取到完整的数据包
            let resultBytes = cache[8..<8+Int(length)]
            let resultData = Data(resultBytes)
            //将数据传给delegate进行处理
            self.delegate?.socketDidRead(type: type, data: resultData,retry:true)
            //沾包循环读取
            let begin = 8+Int(length)
            let end = cache.count
            cache = Array(cache[begin..<end])
        }
        sendSocket?.readData(withTimeout: -1, tag: 0)
    }

主要流程如下
1、将接收的数据保存到缓存数据中。
2、while循环去读数据,直到判断缓存是否小于8。(0-4字节为消息类型,4-8为消息长度)
3、循环中去判断缓存数据是否大于内容的长度,如果大于,则解析掉这条数据,并从缓存中删除,如果缓存数据还有大于8字节长度的数据,则循环读取数据。
如果数据长度小于获取到的内容长度,则说明出现粘包,则继续readData,继续拼接缓存数据。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容