Netty编解码
1、什么是编解码?
在我们计算机网络IO通信的过程中,我们传输的都是二进制字节流,但是我们程序中一般使用的都是字符串、整型等数据类型,所以在传输之前就需要编解码。
特点:如果是向外发送数据,那就需要编码之后再发送,如果是接收到数据,那么就需要解码之后再使用。
2、Netty编解码涉及到的组件。
Netty涉及到编解码的组件有Channel、ChannelHandler、ChannelPipe等。下面我们大概剖析一下这几个组件。
组件的详细解释可参考这篇文章的Netty模块组件
2.1、Channel
网络通道,用于客户端与服务端发送数据的一条道路,在最外层。
2.2、ChannelPipeline
相当于在Channel这条道路上铺了一层管道,以服务端程序为例,如果是数据从服务端到客户端,我们称之为出站,如果是从客户端到服务端,我们称之为入站。
2.3、ChannelHandler
ChannelHandler 是在 ChannelPipeline 上面的一个个节点,这些节点就是我们写netty程序自己实现的逻辑代码。
其中的ChannelOutboundHandler是我们的编码器,ChannelInboundHandler使我们的解码器,如下代码所示:
bootstrap.group(group).channel(NioSocketChannel.class)
.handler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel ch) throws Exception {
ChannelPipeline pipeline = ch.pipeline();
//pipeline.addLast(new StringEncoder());
pipeline.addLast(new ObjectEncoder());
pipeline.addLast(new NettyClientHandler());
}
});
2.4、三个组件之间的关系
如上图所示,Channel 在最外层,相当于是一条道路,ChannelPipeline 相当于在 Channel 上铺了一个管道,ChannelHandler 就是管道中的具体处理方法。
程序运行特点:程序运行时,会从 ChannelPipeline 中逐个通过我们的每一个 ChannelHandler ,如果是出站,则只会经过 ChannelOutboundHandler 这个类,如果是入站,则只会经过 ChannelInboundHandler 这个类。
ChannelPipeline特点:ChannelPipeline 里面相当于存在了一个双向链表,在初始化的时候,就会有一个头结点和尾结点,addLast这个方法就相当于在尾结点之前加上一个 Handler。
3、protostuff编解码
3.1、引入相关依赖
<dependency>
<groupId>com.dyuproject.protostuff</groupId>
<artifactId>protostuff-api</artifactId>
<version>1.0.10</version>
</dependency>
<dependency>
<groupId>com.dyuproject.protostuff</groupId>
<artifactId>protostuff-core</artifactId>
<version>1.0.10</version>
</dependency>
<dependency>
<groupId>com.dyuproject.protostuff</groupId>
<artifactId>protostuff-runtime</artifactId>
<version>1.0.10</version>
</dependency>
3.2、工具类代码
参考下面代码,在我们具体的handler处理器中调用也是一样的。
public class ProtostuffUtil {
private static Map<Class<?>, Schema<?>> cachedSchema = new ConcurrentHashMap<Class<?>, Schema<?>>();
private static <T> Schema<T> getSchema(Class<T> clazz) {
@SuppressWarnings("unchecked")
Schema<T> schema = (Schema<T>) cachedSchema.get(clazz);
if (schema == null) {
schema = RuntimeSchema.getSchema(clazz);
if (schema != null) {
cachedSchema.put(clazz, schema);
}
}
return schema;
}
/**
* 序列化
*
* @param obj
* @return
*/
public static <T> byte[] serializer(T obj) {
@SuppressWarnings("unchecked")
Class<T> clazz = (Class<T>) obj.getClass();
LinkedBuffer buffer = LinkedBuffer.allocate(LinkedBuffer.DEFAULT_BUFFER_SIZE);
try {
Schema<T> schema = getSchema(clazz);
return ProtostuffIOUtil.toByteArray(obj, schema, buffer);
} catch (Exception e) {
throw new IllegalStateException(e.getMessage(), e);
} finally {
buffer.clear();
}
}
/**
* 反序列化
*
* @param data
* @param clazz
* @return
*/
public static <T> T deserializer(byte[] data, Class<T> clazz) {
try {
T obj = clazz.newInstance();
Schema<T> schema = getSchema(clazz);
ProtostuffIOUtil.mergeFrom(data, obj, schema);
return obj;
} catch (Exception e) {
throw new IllegalStateException(e.getMessage(), e);
}
}
public static void main(String[] args) {
byte[] userBytes = ProtostuffUtil.serializer(new User(1, "zhuge"));
User user = ProtostuffUtil.deserializer(userBytes, User.class);
System.out.println(user);
}
}