netty 自定义协议
自定义协议
Message
@Data
public class Message {
private int magic; //魔数
private int version; //消息版本
private byte classType; //类:0表示Person、1表示Product
private int length; //消息体的长度
private byte[] body; //消息体内容
}
ClassType
public class ClassType {
public static Map<Byte,Class<?>> getType(){
Map<Byte, Class<?>> type = new HashMap<>();
type.put((byte)0, Person.class);
type.put((byte)1, Product.class);
return type;
}
}
Person
@Data
public class Person {
private String name;
private Integer age;
}
Product
@Data
public class Product {
private String name;
private Float price;
private Integer num;
}
自定义编解码器
CustomMessageEncoder
public class CustomMessageEncoder extends MessageToByteEncoder<Message> {
@Override
protected void encode(ChannelHandlerContext channelHandlerContext, Message message, ByteBuf byteBuf) throws Exception {
byteBuf.writeInt(message.getMagic());
byteBuf.writeInt(message.getVersion());
byteBuf.writeByte(message.getClassType());
byteBuf.writeInt(message.getLength());
byteBuf.writeBytes(message.getBody());
}
}
CustomMessageDecoder
public class CustomMessageDecoder extends ByteToMessageDecoder {
@Override
protected void decode(ChannelHandlerContext channelHandlerContext, ByteBuf byteBuf, List<Object> list) throws Exception {
Message message = new Message();
int magic = byteBuf.readInt();
if (magic != 100000){
channelHandlerContext.channel().close();
return;
}
int version = byteBuf.readInt();
byte classType = byteBuf.readByte();
int length = byteBuf.readInt();
byte[] body = new byte[length];
byteBuf.readBytes(body);
message.setMagic(magic);
message.setVersion(version);
message.setClassType(classType);
message.setLength(length);
message.setBody(body);
list.add(message);
switch (classType){
case 0:{
System.out.println("服务端接收到person数据");
Person person = JSON.parseObject(body, ClassType.getType().get((byte)0));
System.out.println(person);
} break;
case 1:{
System.out.println("服务端接收到product数据");
Product product = JSON.parseObject(body, Product.class);
System.out.println(product);
}
}
}
}
服务端
CustomServerHandler
public class CustomServerHandler extends SimpleChannelInboundHandler<Message> {
@Override
protected void channelRead0(ChannelHandlerContext channelHandlerContext, Message message) throws Exception {
System.out.println("服务端接收到的message对象为:"+message);
}
}
NettyServer
public class NettyServer {
public static void startServer(int port){
EventLoopGroup bossGroup = new NioEventLoopGroup();
EventLoopGroup workerGroup = new NioEventLoopGroup();
try {
ServerBootstrap serverBootstrap = new ServerBootstrap();
serverBootstrap.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class)
.option(ChannelOption.SO_BACKLOG, 128)
.childOption(ChannelOption.SO_KEEPALIVE, true)
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel socketChannel) throws Exception {
ChannelPipeline channelPipeline = socketChannel.pipeline();
channelPipeline.addLast(new CustomMessageDecoder());
channelPipeline.addLast(new CustomServerHandler());
}
});
ChannelFuture channelFuture = serverBootstrap.bind(port).sync();
channelFuture.channel().closeFuture().sync();
}catch (Exception e){
e.printStackTrace();
}finally {
bossGroup.shutdownGracefully();
workerGroup.shutdownGracefully();
}
}
public static void main(String[] args) {
startServer(8000);
}
}
客户端
CustomClientHandler
public class CustomClientHandler extends ChannelInboundHandlerAdapter {
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
System.out.println("客户端开始发送person数据");
Message message = new Message();
message.setMagic(100000);
message.setVersion(1);
message.setClassType((byte)0);
Person person = new Person();
person.setName("瓜田李下");
person.setAge(20);
byte[] bytes = JSON.toJSONBytes(person);
message.setLength(bytes.length);
message.setBody(bytes);
ctx.channel().writeAndFlush(message);
System.out.println("客户端开始发送product数据");
Message message2 = new Message();
message2.setMagic(100000);
message2.setVersion(1);
message2.setClassType((byte)1);
Product product = new Product();
product.setName("苹果");
product.setPrice(4f);
product.setNum(100);
byte[] bytes2 = JSON.toJSONBytes(product);
message2.setLength(bytes2.length);
message2.setBody(bytes2);
ctx.channel().writeAndFlush(message2);
}
}
NettyClient
public class NettyClient {
public static void connect(String host, int port){
EventLoopGroup eventLoopGroup = new NioEventLoopGroup();
try {
Bootstrap bootstrap = new Bootstrap();
bootstrap.group(eventLoopGroup)
.channel(NioSocketChannel.class)
.option(ChannelOption.SO_KEEPALIVE, true)
.handler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel socketChannel) throws Exception {
ChannelPipeline channelPipeline = socketChannel.pipeline();
channelPipeline.addLast(new CustomMessageEncoder());
channelPipeline.addLast(new CustomClientHandler());
}
});
ChannelFuture channelFuture = bootstrap.connect(host,port).sync();
channelFuture.channel().closeFuture().sync();
}catch (Exception e){
e.printStackTrace();
}finally {
eventLoopGroup.shutdownGracefully();
}
}
public static void main(String[] args) {
String host = "localhost";
int port = 8000;
connect(host, port);
}
}
使用测试
点击运行后,服务端输出:
08:51:09.785 [nioEventLoopGroup-3-1] DEBUG io.netty.util.Recycler - -Dio.netty.recycler.ratio: 8
08:51:09.785 [nioEventLoopGroup-3-1] DEBUG io.netty.util.Recycler - -Dio.netty.recycler.chunkSize: 32
08:51:09.785 [nioEventLoopGroup-3-1] DEBUG io.netty.util.Recycler - -Dio.netty.recycler.blocking: false
08:51:09.803 [nioEventLoopGroup-3-1] DEBUG io.netty.buffer.AbstractByteBuf - -Dio.netty.buffer.checkAccessible: true
08:51:09.803 [nioEventLoopGroup-3-1] DEBUG io.netty.buffer.AbstractByteBuf - -Dio.netty.buffer.checkBounds: true
08:51:09.808 [nioEventLoopGroup-3-1] DEBUG io.netty.util.ResourceLeakDetectorFactory - Loaded default ResourceLeakDetector: io.netty.util.ResourceLeakDetector@63ce0f70
服务端接收到person数据
Person(name=瓜田李下, age=20)
服务端接收到的message对象为:Message(magic=100000, version=1, classType=0, length=32, body=[123, 34, 97, 103, 101, 34, 58, 50, 48, 44, 34, 110, 97, 109, 101, 34, 58, 34, -25, -109, -100, -25, -108, -80, -26, -99, -114, -28, -72, -117, 34, 125])
服务端接收到product数据
Product(name=苹果, price=4.0, num=100)
服务端接收到的message对象为:Message(magic=100000, version=1, classType=1, length=39, body=[123, 34, 110, 97, 109, 101, 34, 58, 34, -24, -117, -71, -26, -98, -100, 34, 44, 34, 110, 117, 109, 34, 58, 49, 48, 48, 44, 34, 112, 114, 105, 99, 101, 34, 58, 52, 46, 48, 125])
点击运行后,客户端输出:
08:51:09.352 [main] DEBUG io.netty.buffer.PooledByteBufAllocator - -Dio.netty.allocator.cacheTrimIntervalMillis: 0
08:51:09.352 [main] DEBUG io.netty.buffer.PooledByteBufAllocator - -Dio.netty.allocator.useCacheForAllThreads: false
08:51:09.353 [main] DEBUG io.netty.buffer.PooledByteBufAllocator - -Dio.netty.allocator.maxCachedByteBuffersPerChunk: 1023
08:51:09.380 [main] DEBUG io.netty.buffer.ByteBufUtil - -Dio.netty.allocator.type: pooled
08:51:09.380 [main] DEBUG io.netty.buffer.ByteBufUtil - -Dio.netty.threadLocalDirectBufferSize: 0
08:51:09.381 [main] DEBUG io.netty.buffer.ByteBufUtil - -Dio.netty.maxThreadLocalCharBufferSize: 16384
客户端开始发送person数据
08:51:09.743 [nioEventLoopGroup-2-1] DEBUG io.netty.util.Recycler - -Dio.netty.recycler.maxCapacityPerThread: 4096
08:51:09.744 [nioEventLoopGroup-2-1] DEBUG io.netty.util.Recycler - -Dio.netty.recycler.ratio: 8
08:51:09.744 [nioEventLoopGroup-2-1] DEBUG io.netty.util.Recycler - -Dio.netty.recycler.chunkSize: 32
08:51:09.744 [nioEventLoopGroup-2-1] DEBUG io.netty.util.Recycler - -Dio.netty.recycler.blocking: false
08:51:09.758 [nioEventLoopGroup-2-1] DEBUG io.netty.buffer.AbstractByteBuf - -Dio.netty.buffer.checkAccessible: true
08:51:09.762 [nioEventLoopGroup-2-1] DEBUG io.netty.buffer.AbstractByteBuf - -Dio.netty.buffer.checkBounds: true
08:51:09.765 [nioEventLoopGroup-2-1] DEBUG io.netty.util.ResourceLeakDetectorFactory - Loaded default ResourceLeakDetector: io.netty.util.ResourceLeakDetector@6ed2763b
客户端开始发送product数据