netty粘包(二)ObjectDecoder & ObjectEncode
参考:
https://blog.csdn.net/linwei_1029/article/details/49420391
Netty实现消息推送以及内部心跳机制
服务端:
package com.jds.test.pack2;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.FixedLengthFrameDecoder;
import io.netty.handler.codec.LengthFieldBasedFrameDecoder;
import io.netty.handler.codec.LengthFieldPrepender;
import io.netty.handler.codec.protobuf.ProtobufDecoder;
import io.netty.handler.codec.serialization.ClassResolvers;
import io.netty.handler.codec.serialization.ObjectDecoder;
import io.netty.handler.codec.serialization.ObjectEncoder;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.codec.string.StringEncoder;
/**
* 两端SimpleChannelInboundHandler+String编解码+FixedLengthFrameDecoder定长拆包
* Created by sunyuming on 18/8/1.
*/
public class Server4 {
public static void main(String[] args) {
//boss线程监听端口,worker线程负责数据读写
EventLoopGroup boss = new NioEventLoopGroup();
EventLoopGroup worker = new NioEventLoopGroup();
try{
//辅助启动类
ServerBootstrap bootstrap = new ServerBootstrap();
//设置线程池
bootstrap.group(boss,worker);
//设置socket工厂
bootstrap.channel(NioServerSocketChannel.class);
//设置管道工厂
bootstrap.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel socketChannel) throws Exception {
//获取管道
ChannelPipeline pipeline = socketChannel.pipeline();
//定长解码类
// pipeline.addLast(new LengthFieldBasedFrameDecoder(100000, 0, 4, 0, 4));
// pipeline.addLast(new LengthFieldPrepender(4));
//字符串解码类
pipeline.addLast(new ObjectDecoder(ClassResolvers.cacheDisabled(null)));
pipeline.addLast(new ObjectEncoder());
//处理类
pipeline.addLast(new ServerHandler4());
}
});
//绑定端口
ChannelFuture future = bootstrap.bind(8866).sync();
System.out.println("server start ...... ");
//等待服务端监听端口关闭
future.channel().closeFuture().sync();
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
//优雅退出,释放线程池资源
boss.shutdownGracefully();
worker.shutdownGracefully();
}
}
}
package com.jds.test.pack2;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
/**
* Created by sunyuming on 18/8/1.
*/
class ServerHandler4 extends SimpleChannelInboundHandler<BaseBean> {
//用于记录次数
private int count = 0;
//读取客户端发送的数据
@Override
protected void channelRead0(ChannelHandlerContext ctx, BaseBean msg) throws Exception {
System.out.println("RESPONSE--------"+msg.toString());
ctx.writeAndFlush(msg);
}
//新客户端接入
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
System.out.println("channelActive");
}
//客户端断开
@Override
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
System.out.println("channelInactive");
}
//异常
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
//关闭通道
ctx.channel().close();
//打印异常
cause.printStackTrace();
}
}
客户端:
package com.jds.test.pack2;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.FixedLengthFrameDecoder;
import io.netty.handler.codec.LengthFieldBasedFrameDecoder;
import io.netty.handler.codec.LengthFieldPrepender;
import io.netty.handler.codec.serialization.ClassResolvers;
import io.netty.handler.codec.serialization.ObjectDecoder;
import io.netty.handler.codec.serialization.ObjectEncoder;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.codec.string.StringEncoder;
import java.net.InetSocketAddress;
/**
* Created by sunyuming on 18/8/1.
*/
public class Client4 {
public static void main(String[] args) {
//worker负责读写数据
EventLoopGroup worker = new NioEventLoopGroup();
try {
//辅助启动类
Bootstrap bootstrap = new Bootstrap();
//设置线程池
bootstrap.group(worker);
//设置socket工厂
bootstrap.channel(NioSocketChannel.class);
//设置管道
bootstrap.handler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel socketChannel) throws Exception {
//获取管道
ChannelPipeline pipeline = socketChannel.pipeline();
//定长解码类
// pipeline.addLast(new LengthFieldBasedFrameDecoder(100000, 0, 4, 0, 4));
// pipeline.addLast(new LengthFieldPrepender(4));
//字符串解码类
pipeline.addLast(new ObjectDecoder(ClassResolvers.cacheDisabled(null)));
pipeline.addLast(new ObjectEncoder());
//处理类
pipeline.addLast(new ClientHandler4());
}
});
//发起异步连接操作
ChannelFuture futrue = bootstrap.connect(new InetSocketAddress("127.0.0.1",8866)).sync();
//等待客户端链路关闭
futrue.channel().closeFuture().sync();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
//优雅的退出,释放NIO线程组
worker.shutdownGracefully();
}
}
}
package com.jds.test.pack2;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
/**
* Created by sunyuming on 18/8/1.
*/
class ClientHandler4 extends SimpleChannelInboundHandler<BaseBean> {
//接受服务端发来的消息
@Override
protected void channelRead0(ChannelHandlerContext ctx, BaseBean msg) throws Exception {
System.out.println("server response : "+msg.toString());
}
//与服务器建立连接
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
//给服务器发消息
//发送50次消息
for (int i = 0; i < 5; i++) {
ctx.channel().writeAndFlush(new BaseBean());
}
}
//与服务器断开连接
@Override
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
System.out.println("channelInactive");
}
//异常
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
//关闭管道
ctx.channel().close();
//打印异常信息
cause.printStackTrace();
}
}
对象:
package com.jds.test.pack2;
import java.io.Serializable;
/**
* Created by sunyuming on 18/8/1.
*/
public class BaseBean implements Serializable {
private static int seria = 0;
private static String base = "abcdefghigklmn";
public BaseBean() {
setNo(++seria);
setRandom(base.substring(seria));
}
private Integer no;
private String random;
@Override
public String toString() {
return String.valueOf(no) + ":" + random;
}
public Integer getNo() {
return no;
}
public void setNo(Integer no) {
this.no = no;
}
public String getRandom() {
return random;
}
public void setRandom(String random) {
this.random = random;
}
}
服务端输出:
server start ......
channelActive
RESPONSE--------1:bcdefghigklmn
RESPONSE--------2:cdefghigklmn
RESPONSE--------3:defghigklmn
RESPONSE--------4:efghigklmn
RESPONSE--------5:fghigklmn
客户端:
server response : 1:bcdefghigklmn
server response : 2:cdefghigklmn
server response : 3:defghigklmn
server response : 4:efghigklmn
server response : 5:fghigklmn
注意点:
1.Object编解码已经处理了粘拆包:
public class ObjectDecoder extends LengthFieldBasedFrameDecoder
public ObjectDecoder(ClassResolver classResolver) {
this(1048576, classResolver);
}
public ObjectDecoder(int maxObjectSize, ClassResolver classResolver) {
super(maxObjectSize, 0, 4, 0, 4);
this.classResolver = classResolver;
}
******************************************
public class ObjectEncoder extends MessageToByteEncoder<Serializable> {
private static final byte[] LENGTH_PLACEHOLDER = new byte[4];
public ObjectEncoder() {
}
protected void encode(ChannelHandlerContext ctx, Serializable msg, ByteBuf out) throws Exception {
int startIdx = out.writerIndex();
ByteBufOutputStream bout = new ByteBufOutputStream(out);
bout.write(LENGTH_PLACEHOLDER);
替代了:
// 解码 pipeline.addLast(new LengthFieldBasedFrameDecoder(100000, 0, 4, 0, 4));
// 编码 pipeline.addLast(new LengthFieldPrepender(4));
句的功效
2.LengthFieldBasedFrameDecoder与LengthFieldPrepender 与自定义编解码可不共存?
自定义可参考:https://www.cnblogs.com/hupengcool/p/3931721.html
关键代码:


红色的为自己处理合包和拆包
当然我们Netty也有自带的LengthFieldBasedFrameDecoder,但是在使用自定义序列化的时候,我觉得还是自己写比较方便一点,反正总不是要写代码。
3. ObjectEncoder来实现自定义对象的序列化,但是用的是java内置的序列化,必须实现Serializable接口,实测。
浙公网安备 33010602011771号