Netty4 自定义Decoder,Encoder进行对象传递

   首先我们必须知道Tcp粘包和拆包的,TCP是个“流”协议,所谓流,就是没有界限的一串数据,TCP底层并不了解上层业务数据的具体含义,它会根据TCP缓冲区的实际数据进行包的划分,一个完整的包可能会被拆分成多个包进行发送,也有可能把多个小的包封装成一个大的数据包进行发送。这里引用Netty官网的User guide里面的图进行说明:

Dealing with a Stream-based Transport

One Small Caveat of Socket Buffer

In a stream-based transport such as TCP/IP, received data is stored into a socket receive buffer. Unfortunately, the buffer of a stream-based transport is not a queue of packets but a queue of bytes. It means, even if you sent two messages as two independent packets, an operating system will not treat them as two messages but as just a bunch of bytes. Therefore, there is no guarantee that what you read is exactly what your remote peer wrote. For example, let us assume that the TCP/IP stack of an operating system has received three packets:

Because of this general property of a stream-based protocol, there‘s high chance of reading them in the following fragmented form in your application:

Therefore, a receiving part, regardless it is server-side or client-side, should defrag the received data into one or more meaningful frames that could be easily understood by the application logic. In case of the example above, the received data should be framed like the following:

  那么一般情况下我们是如何解决这种问题的呢?我所知道的有这几种方案:

    >1.消息定长

    >2.在包尾增加一个标识,通过这个标志符进行分割

    >3.将消息分为两部分,也就是消息头和消息尾,消息头中写入要发送数据的总长度,通常是在消息头的第一个字段使用int值来标识发送数据的长度。

  这里以第三种方式为例,进行对象传输。Netty4本身自带了ObjectDecoder,ObjectEncoder来实现自定义对象的序列化,但是用的是java内置的序列化,由于java序列化的性能并不是很好,所以很多时候我们需要用其他序列化方式,常见的有Kryo,Jackson,fastjson,protobuf等。这里要写的其实用什么序列化不是重点,而是我们怎么设计我们的Decoder和Encoder。

  首先我们写一个Encoder,我们继承自MessageToByteEncoder<T> ,把对象转换成byte,继承这个对象,会要求我们实现一个encode方法:

    @Override
    protected void encode(ChannelHandlerContext ctx, Object msg, ByteBuf out) throws Exception {
        byte[] body = convertToBytes(msg);  //将对象转换为byte,伪代码,具体用什么进行序列化,你们自行选择。可以使用我上面说的一些
        int dataLength = body.length;  //读取消息的长度
        out.writeInt(dataLength);  //先将消息长度写入,也就是消息头
        out.writeBytes(body);  //消息体中包含我们要发送的数据
    }

  那么当我们在Decode的时候,该怎么处理发送过来的数据呢?这里我们继承ByteToMessageDecoder方法,继承这个对象,会要求我们实现一个decode方法

  

public void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) {
        if (in.readableBytes() < HEAD_LENGTH) {  //这个HEAD_LENGTH是我们用于表示头长度的字节数。  由于上面我们传的是一个int类型的值,所以这里HEAD_LENGTH的值为4.
            return;
        }
        in.markReaderIndex();                  //我们标记一下当前的readIndex的位置
        int dataLength = in.readInt();       // 读取传送过来的消息的长度。ByteBuf 的readInt()方法会让他的readIndex增加4
        if (dataLength < 0) { // 我们读到的消息体长度为0,这是不应该出现的情况,这里出现这情况,关闭连接。
            ctx.close();
        }

        if (in.readableBytes() < dataLength) { //读到的消息体长度如果小于我们传送过来的消息长度,则resetReaderIndex. 这个配合markReaderIndex使用的。把readIndex重置到mark的地方
            in.resetReaderIndex();
            return;
        }

        byte[] body = new byte[dataLength];  //  嗯,这时候,我们读到的长度,满足我们的要求了,把传送过来的数据,取出来吧~~
        in.readBytes(body);  //
        Object o = convertToObject(body);  //将byte数据转化为我们需要的对象。伪代码,用什么序列化,自行选择
        out.add(o);
    }

  当然我们Netty也有自带的LengthFieldBasedFrameDecoder,但是在使用自定义序列化的时候,我觉得还是自己写比较方便一点,反正总不是要写代码。

  我走过的坑:用读取ByteBuf的使用,一定要注意,其中的方法是否会增加readIndex,不然的话会造成无法正常读到我们想要的数据。

时间: 2024-10-25 21:09:12

Netty4 自定义Decoder,Encoder进行对象传递的相关文章

自定义Encoder/Decoder进行对象传递

转载:http://blog.csdn.net/top_code/article/details/50901623 在上一篇文章中,我们使用Netty4本身自带的ObjectDecoder,ObjectEncoder来实现POJO对象的传输,但其使用的是Java内置的序列化,由于Java序列化的性能并不是很好,所以很多时候我们需要用其他高效的序列化方式,例如 protobuf,Hessian, Kryo,Jackson,fastjson等. 本文中Java序列化不是重点,对Java序列化不熟悉的

java的对象锁和对象传递

1.对象传递 在JAVA的参数传递中,有两种类型,第一种是基本类型传递,例如int,float,double等,这种是值传递,另外一种是对象传递,比如String或者自定义的类,这种是引用传递. 也就是说,基本类型传递的是一个副本,而对象传递的是对象本身. 2.锁 JAVA中,对象锁的概念,就是对对象进行加锁,每个对象都会有一个内存锁,如果加上锁以后,就只能让一个线程进行操作,在操作完成之前,其他线程无法对该对象进行再次操作. 3.例子 package com.itbuluoge.mythrea

Feign发送Get请求时,采用POJO对象传递参数的最终解决方案 Request method &#39;POST&#39; not supported (附带其余好几个坑)

yml: feign: httpclient: enabled: true properties: #feign feign.httpclient.enabled=true <!-- https://mvnrepository.com/artifact/io.github.openfeign/feign-httpclient --> <dependency> <groupId>io.github.openfeign</groupId> <artifac

c#进程之间对象传递方法

1. 起源 KV项目下载底层重构升级决定采用独立进程进行Media下载处理,以能做到模块复用之目的,因此涉及到了独立进程间的数据传递问题. 目前进程间数据传递,多用WM_COPYDATA.共享dll.内存映射.Remoting等方式.相对来说,WM_COPYDATA方式更为简便,网上更到处是其使用方法. 而且Marshal这个静态类,其内置多种方法,可以很方便实现字符串.结构体等数据在不同进程间传递. 那么,对象呢?如何传递? 2.序列化 想到了,Newtonsoft.Json.dll这个神器.

Oracle自定义数据类型 2 (调用对象方法)

调用对象方法 调用对象方法基于类型创建表后,就可以在查询中调用对象方法 A. 创建基于对象的表语法: create   table   <表名>   of   <对象类型>意义:此表具有该类型和member方法的所有属性, 我们不能通过DBA   STUDIO的表数据编辑器来编辑数据. 例:create   table   FLIGHT_SCH_TAB   of   FLIGHT_SCH_TYPE insert   into   FLIGHT_SCH_TAB     values(

Servlet页面间对象传递的方法

Servlet页面间对象传递的方法 1.request 2.session 3.application 4.cookie 5.其他的 Servlet页面间对象传递的方法,布布扣,bubuko.com

java对象传递小解析

先上代码: 1 import org.apache.commons.lang.builder.ToStringBuilder; 2 import org.apache.commons.lang.builder.ToStringStyle; 3 import org.slf4j.Logger; 4 import org.slf4j.LoggerFactory; 5 6 /** 7 * 引用传递demo 8 * @author lishaohua 9 * time : 2016-3-24 下午5:1

使用HashMap对象传递url参数实用工具类

代码如下: package com.yanek.util; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; public class HttpUtil { /** * @param args

asp.net中利用session对象传递、共享数据[session用法]

下面介绍Asp.net中利用session对象传递.共享数据用法: 1.传递值: 首先定义将一个文本值或单独一个值赋予session,如下: session["name"]=textbox1.text:将文本1的值赋给了session变量name,当调查到其它页面时,此值可以传递,依然存在,下面是调用或判断此值. If(session["name"]==null) {} Else { lable1.text=session["name"].tos