美文网首页
消息粘包 和 消息不完整 问题

消息粘包 和 消息不完整 问题

作者: Aska小强 | 来源:发表于2022-03-30 10:57 被阅读0次

    消息粘包 和 消息不完整 问题

    消息粘包 和 消息不完整问题 其实都是应用层会带来的问题,和TCP 没关系,TCP 是能够保证消息的顺序 和 完整性的

    本篇只是简单说明一下 什么是 消息粘包 和 消息不完整问题

    image-20220330104603542

    1.复现消息粘包 和 消息不完成 问题

    先来看看 出现了什么问题导致 需要去处理 消息粘包 和 消息完整 问题 ,前面通过NIO改造了聊天室的案例中,我们开复现一下 消息粘包 和 消息不完整

    1.1 复现 消息粘包问题

    Client端发送多条数据

    代码还是原来的Client端代码 ,只是在发送数据的时候 一次性发了100条

    public static void connectServer(ServerInfo serverInfo) {
            try {
                // 开启 tcp 连接
                socket = new Socket(Inet4Address.getByName(serverInfo.getIp()), serverInfo.getPort());
                // 开启 线程 异步 读取 server message
                clientReadHandler =
                        new ClientReadHandler(socket.getInputStream(), ClientConnectTcp::close);
                clientReadHandler.start();
                // 监听 键盘写入 system.in
                systemInReader = new BufferedReader(new InputStreamReader(System.in));
                clientWriteHandler = new ClientWriteHandler(socket.getOutputStream());
                do {
                    String message = systemInReader.readLine();
                    // 异步发送 到 server
                    if (message != null) {
                         //把读取到的message 发送100次 并且后面添加上i标识
                        for (int i = 0; i < 100; i++) {
                            clientWriteHandler.sendMsg(message + ":" + i);
                        }
                    }
                    if (CommonConstants.BYE_FLAG.equals(message)) {
                        close();
                    }
                } while (!doReadFlag);
            } catch (IOException e) {
                log.error("【建立tcp 连接异常:{}】", e.getMessage());
            } finally {
                close();
            }
        }
    

    Server端 接受到的消息

    可以看到 出现了严重的粘包问题,原本我们希望 消息是一条一条处理,如下:

    receiveAsync message:abcdefg:0

    receiveAsync message:abcdefg:1

    receiveAsync message:abcdefg:2

    10:12:21.721 [read-io-executors1] INFO com.johnny.chatroom.lib.nio.Connector - receiveAsync message:abcdefg:0
    abcdefg:1
    abcdefg:2
    abcdefg:3
    abcdefg:4
    abcdefg:5
    abcdefg:6
    abcdefg:7
    abcdefg:8
    abcdefg:9
    abcdefg:10
    abcdefg:11
    abcdefg:12
    abcdefg:13
    abcdefg:14
    abcdefg:15
    abcdefg:16
    abcdefg:17
    abcdefg:18
    abcdefg:19
    abcdefg:20
    abcdefg:21
    abcdefg:22
    abcdefg:23
    a
    10:12:21.722 [read-io-executors2] INFO com.johnny.chatroom.lib.nio.Connector - receiveAsync message:cdefg:24
    abcdefg:25
    abcdefg:26
    

    1.2 复现 消息不完整问题

    修改Server 服务器端的 IoArgs 的 ByteBuffer的 缓冲区大小

    @Slf4j
    @Data
    public class IoArgs {
      //缓冲区大小 从 256个字节  改成 4 个字节
      private ByteBuffer byteBuffer = ByteBuffer.allocate(4);
      其他代码省略...
    }  
    

    Client客户端还是如下 发送100条数据

    do {
           String message = systemInReader.readLine();
           // 异步发送 到 server
           if (message != null) {
               for (int i = 0; i < 100; i++) {
                  clientWriteHandler.sendMsg(message + ":" + i);
              }
           }
           if (CommonConstants.BYE_FLAG.equals(message)) {
               close();
           }
     } while (!doReadFlag);
    

    Server端 接受到的消息

    可以看到 原本一条消息 abcdefg 被拆开成了 很多子消息了。。出现了 严重的 消息不完整问题

    10:19:38.754 [read-io-executors1] INFO com.johnny.chatroom.lib.nio.Connector - receiveAsync message:abc
    10:19:38.754 [read-io-executors2] INFO com.johnny.chatroom.lib.nio.Connector - receiveAsync message:efg
    10:19:38.755 [read-io-executors3] INFO com.johnny.chatroom.lib.nio.Connector - receiveAsync message:0
    a
    10:19:38.755 [read-io-executors4] INFO com.johnny.chatroom.lib.nio.Connector - receiveAsync message:cde
    10:19:38.756 [read-io-executors1] INFO com.johnny.chatroom.lib.nio.Connector - receiveAsync message:g:1
    10:19:38.756 [read-io-executors2] INFO com.johnny.chatroom.lib.nio.Connector - receiveAsync message:abc
    10:19:38.756 [read-io-executors3] INFO com.johnny.chatroom.lib.nio.Connector - receiveAsync message:efg
    10:19:38.756 [read-io-executors4] INFO com.johnny.chatroom.lib.nio.Connector - receiveAsync message:2
    a
    10:19:38.756 [read-io-executors1] INFO com.johnny.chatroom.lib.nio.Connector - receiveAsync message:cde
    10:19:38.757 [read-io-executors2] INFO com.johnny.chatroom.lib.nio.Connector - receiveAsync message:g:3
    10:19:38.757 [read-io-executors3] INFO com.johnny.chatroom.lib.nio.Connector - receiveAsync message:abc
    10:19:38.757 [read-io-executors4] INFO com.johnny.chatroom.lib.nio.Connector - receiveAsync message:efg
    10:19:38.757 [read-io-executors1] INFO com.johnny.chatroom.lib.nio.Connector - receiveAsync message:4
    
    

    2. 消息粘包 和 消息不完成 问题概述

    在socket网络编程中,都是端到端通信,由客户端端口+服务端端口+客户端IP+服务端IP+传输协议组成的五元组可以明确的标识一条连接。在TCP的socket编程中,发送端和接收端都有成对的socket。发送端为了将多个发往接收端的包,更加高效的的发给接收端,于是采用了优化算法(Nagle算法)将多次间隔较小、数据量较小的数据,合并成一个数据量大的数据块,然后进行封包。那么这样一来,接收端就必须使用高效科学的拆包机制来分辨这些数据。

    2.1 什么是TCP粘包问题?

    TCP粘包就是指发送方发送的若干包数据到达接收方时粘成了一包,从接收缓冲区来看,后一包数据的头紧接着前一包数据的尾,出现粘包的原因是多方面的,可能是来自发送方,也可能是来自接收方。

    2.2 造成TCP粘包的原因

    • 发送方原因

      TCP默认使用Nagle算法(主要作用:减少网络中报文段的数量),而Nagle算法主要做两件事:

      只有上一个分组得到确认,才会发送下一个分组
      收集多个小分组,在一个确认到来时一起发送
      Nagle算法造成了发送方可能会出现粘包问题

    • 接收方原因

      TCP接收到数据包时,并不会马上交到应用层进行处理,或者说应用层并不会立即处理。实际上,TCP将接收到的数据包保存在接收缓存里,然后应用程序主动从缓存读取收到的分组。这样一来,如果TCP接收数据包到缓存的速度大于应用程序从缓存中读取数据包的速度,多个包就会被缓存,应用程序就有可能读取到多个首尾相接粘到一起的包。

    • 什么时候需要处理粘包现象?

      如果发送方发送的多组数据本来就是同一块数据的不同部分,比如说一个文件被分成多个部分发送,这时当然不需要处理粘包现象
      如果多个分组毫不相干,甚至是并列关系,那么这个时候就一定要处理粘包现象了

    2.3 如何处理粘包现象?

    • 发送方

      对于发送方造成的粘包问题,可以通过关闭Nagle算法来解决,使用TCP_NODELAY选项来关闭算法

    • 接收方

      接收方没有办法来处理粘包现象,只能将问题交给应用层来处理

    • 应用层

      应用层的解决办法简单可行,不仅能解决接收方的粘包问题,还可以解决发送方的粘包问题。

      1.固定包长度的数据包

      2.以指定字符(串)为包的结束标志 如换行符 \n

      3.包头 + 包体格式 这种格式的包一般分为两部分,即包头和包体,包头是固定大小的,且包头中必须含有一个字段来说明接下来的包体有多大。

    总结

    本篇简单概述了一下 什么是 消息粘包 和 消息不完整问题,并且通过代码 复现了一下出现的问题,那么具体的处理粘包等问题,后面再写,核心思想就是 通过读取包头获取要读取的数据包的长度,然后根据长度去读取后面的数据,不够就先缓存 等待下一个包来,足够了长度就丢给上层处理,既解决了 消息粘包 也能解决消息不完整问题,具体代码演示 下一篇再述

    欢迎大家访问 个人博客 Johnny小屋

    相关文章

      网友评论

          本文标题:消息粘包 和 消息不完整 问题

          本文链接:https://www.haomeiwen.com/subject/cpvijrtx.html