实例:Netty 处理 TCP协议数据分包问题

一、Netty解决TCP协议数据分包问题思路


我们知道通过TCP协议发送接收数据时,如果数据过大,接收到的数据会是分包的,比如:
                                     +-----+-----+-----+
         发送数据是: | ABC | DEF | GHI |
                            +-----+-----+-----+
         而我们想接受到的数据是: | ABCDEFGHI |
                    
该如何处理这种情况呢?Netty提供了一个专门处理TCP协议数据的Handler: LengthFieldBasedFrameDecoder它的原理是服务器端和客户端约定一个协议格式:数据包=协议长度+协议体

      --------------------------------数据包------------------------------

     | 协议长度部分(接收数据长度) | 协议体部分(要接收的数据)|

举个例子,假如我们的TCP客户端发送了10MB字节的数据,如何让Netty服务器一次就接收到这10MB数据呢?那就需要客户端告诉服务端我发送的数据大小是多少,即在发送的数据中加入一个“数据包长度”即可,上面提到的Handler就是用来和客户端约定这个协议格式的,它有几个参数,下面我介绍一下它的参数意义:
      int  maxFrameLength:定义接收数据包的最大长度,如果发送的数据包超过此值,则抛出异常;
     int  lengthFieldOffset:长度属性部分的偏移值,0表示长度属性位于数据包头部;
     int   lengthFieldLength:长度属性的字节长度,如果设置为4,就是我们用4个字节存放数据包的长度;
      int   lengthAdjustment:协议体长度调节值,修正信息长度,如果设置为4,那么解码时再向后推4个字节;
      int   initialBytesToStrip:跳过字节数,如我们想跳过长度属性部分。

二、实例-客户端发送10MB字节的数据,Netty服务端一次接收到全部10MB数据

客户端:定义一个消息体,用头部四个字节存放数据包长度

  1. public byte[] send(byte[] sendData) throws UnknownHostException, IOException {  
  2.         Socket socket = new Socket(serverIp, serverPort);  
  3.         OutputStream os = socket.getOutputStream();  
  4.         InputStream is = socket.getInputStream();  
  5.         byte resultArray[] = null;  
  6.         try {  
  7.             // 定义一个发送消息协议格式:|--header:4 byte--|--content:10MB--|  
  8.             // 获取一个4字节长度的协议体头  
  9.             byte[] dataLength = intToByteArray(4, sendData.length);  
  10.             // 和请求的数据组成一个请求数据包  
  11.             byte[] requestMessage = combineByteArray(dataLength, sendData);  
  12.             //发送数据-------------------------------  
  13.             os.write(requestMessage);  
  14.             os.flush();  
  15.             //接收数据-------------------------------  
  16.             resultArray = IOUtils.toByteArray(is);    
  17.         } catch (Exception e) {  
  18.             e.printStackTrace();  
  19.         } finally {  
  20.             os.close();  
  21.             is.close();  
  22.             socket.close();  
  23.         }  
  24.         return resultArray;  
  25.     }  
  1. private static byte[] intToByteArray(int byteLength, int intValue) {  
  2.         return ByteBuffer.allocate(byteLength).putInt(intValue).array();  
  3.     }  
  4. private static byte[] combineByteArray(byte[] array1, byte[] array2) {  
  5.         byte[] combined = new byte[array1.length + array2.length];  
  6.         System.arraycopy(array1, 0, combined, 0, array1.length);  
  7.         System.arraycopy(array2, 0, combined, array1.length, array2.length);  
  8.         return combined;  
  9.     }  

Netty服务端:定义一个LengthFieldBasedFrameDecoder(1024*1024*1024, 0, 4,0,4)),最大数据量是1GB,长度属性位于数据包头部,占4个字节,协议体调节值为0,跳过头部协议长度四个字节
  1. @Override  
  2.             public void initChannel(SocketChannel ch) throws Exception {  
  3.                 ChannelPipeline pipeline = ch.pipeline();  
  4.   
  5.                 pipeline.addLast("framedecoder",new LengthFieldBasedFrameDecoder(1024*1024*102404,0,4));  
  6.                 pipeline.addLast(new TCPServiceHandler());// 处理业务Handler  
  7.                   
  8.   
  9.             } 

三、总结:客户端和服务端定义消息格式必须一致

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值