概述
根据消息的流向,处理器可以分为入站处理器和出站处理器,也存在既是出站处理器又是入站处理器的情况,但是不建议这么做。
- 出站处理器
- 顶层为ChannelInboundHandler
- 入站处理器
- 顶层为ChannelOutboundHandler
在处理消息数据时的各种编解码器,本质上都是处理器:无论我们向网络中写入的数据是什么类型(int、char、String、二进制等),其都是以字节流的形式进行的。所以,数据由原本的形式转换为字节流的操作称为编码(encode),将数据由字节流转换为其原本的格式称为解码(decode)。在某种程度上可以理解为:编码是一种出站处理器,解码是一种入站处理器,编解码器称为codec。
自定义处理器
Netty其实内置了很多的处理器供开发者使用,除此之外我们可以根据需要自定义处理器或者是编解码器。
下面是一个简单的自定义编解码器示例,此对编解码器的功能是将netty接收到的message,由Bytebuf转换为Long(入站),或者是Long转换为Bytebuf(出站)
编码器
package com.leolee.netty.handler;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.MessageToByteEncoder;
/**
* @ClassName MyLongToBytebufEncoder
* @Description: 编码器
* @Author LeoLee
* @Date 2021/2/21
* @Version V1.0
**/
public class MyLongToBytebufEncoder extends MessageToByteEncoder<Long> {
@Override
protected void encode(ChannelHandlerContext ctx, Long msg, ByteBuf out) throws Exception {
System.out.println("encode invoked");
System.out.println("msg:" + msg);
out.writeLong(msg);
}
}
解码器
package com.leolee.netty.handler;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.ByteToMessageDecoder;
import java.util.List;
/**
* @ClassName MyByteToLongDecoder
* @Description: 解码器
* @Author LeoLee
* @Date 2021/2/21
* @Version V1.0
**/
public class MyByteToLongDecoder extends ByteToMessageDecoder {
@Override
protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {
System.out.println("decode invoked");
System.out.println("readable bytes num:" + in.readableBytes());
if (in.readableBytes() >= 8) {//long类型是8歌字节
out.add(in.readLong());
}
}
}
客户端
package com.leolee.netty.handler;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioSocketChannel;
/**
* @ClassName MyClient
* @Description: socket客户端
* @Author LeoLee
* @Date 2020/8/23
* @Version V1.0
**/
public class MyClient {
public static void main(String[] args) throws InterruptedException {
//客户端只需要一个线程组
EventLoopGroup eventLoopGroup = new NioEventLoopGroup();
try {
//声明客户端启动类
Bootstrap bootstrap = new Bootstrap();
bootstrap.group(eventLoopGroup).channel(NioSocketChannel.class).handler(new MyClientInitializer());
ChannelFuture channelFuture = bootstrap.connect("localhost", 8899).sync();
channelFuture.channel().closeFuture().sync();
} finally {
//优雅关闭
eventLoopGroup.shutdownGracefully();
}
}
}
package com.leolee.netty.handler;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.socket.SocketChannel;
/**
* @ClassName MyClientInitializer
* @Description:
* @Author LeoLee
* @Date 2020/8/23
* @Version V1.0
**/
public class MyClientInitializer extends ChannelInitializer<SocketChannel> {
@Override
protected void initChannel(SocketChannel ch) throws Exception {
//声明管道
ChannelPipeline pipeline = ch.pipeline();
//编解码器
pipeline.addLast(new MyByteToLongDecoder());
pipeline.addLast(new MyLongToBytebufEncoder());
//自定义处理器
pipeline.addLast("myClientHandler", new MyClientHandler());
}
}
package com.leolee.netty.handler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import java.time.LocalDateTime;
/**
* @ClassName MyClientHandler
* @Description:
* @Author LeoLee
* @Date 2020/8/23
* @Version V1.0
**/
public class MyClientHandler extends SimpleChannelInboundHandler<Long> {
@Override
protected void channelRead0(ChannelHandlerContext ctx, Long msg) throws Exception {
System.out.println(ctx.channel().remoteAddress());
System.out.println("Client output:" + msg);
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
cause.printStackTrace();
ctx.close();
}
/**
* 功能描述: <br> 该回调方法是连接处理活跃状态
* 〈〉由于demo没有办法模拟请求的发送,所以重写这个方法来模拟客户端的消息发送
* @Param: [ctx]
* @Return: void
* @Author: LeoLee
* @Date: 2020/8/23 16:55
*/
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
ctx.writeAndFlush(123456L);
}
}
服务端
package com.leolee.netty.handler;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;
/**
* @ClassName MySocketServer
* @Description: socket服务端
* @Author LeoLee
* @Date 2020/8/23
* @Version V1.0
**/
public class MySocketServer {
public static void main(String[] args) throws InterruptedException {
//定义线程组 EventLoopGroup为死循环
//boss线程组一直在接收客户端发起的请求,但是不对请求做处理,boss会将接收到的请i交给worker线程组来处理
//实际可以用一个线程组来做客户端的请求接收和处理两件事,但是不推荐
EventLoopGroup bossGroup = new NioEventLoopGroup(1);
EventLoopGroup workerGroup = new NioEventLoopGroup();
try {
//启动类定义
ServerBootstrap serverBootstrap = new ServerBootstrap();
serverBootstrap.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class)
//子处理器,自定义处理器,服务端可以使用childHandler或者handler,handlerr对应接收线程组(bossGroup),childHandler对应处理线程组(workerGroup)
.childHandler(new MySocketServerInitializer());
//绑定监听端口
ChannelFuture channelFuture = serverBootstrap.bind(8899).sync();
//定义关闭监听
channelFuture.channel().closeFuture().sync();
} finally {
//Netty提供的优雅关闭
bossGroup.shutdownGracefully();
workerGroup.shutdownGracefully();
}
}
}
package com.leolee.netty.handler;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.socket.SocketChannel;
/**
* @ClassName MySocketServerInitializer
* @Description: 一旦客户端和服务端建立联系之后initChannel就会被调用
* @Author LeoLee
* @Date 2020/8/23
* @Version V1.0
**/
public class MySocketServerInitializer extends ChannelInitializer<SocketChannel> {
@Override
protected void initChannel(SocketChannel ch) throws Exception {
//声明管道
ChannelPipeline pipeline = ch.pipeline();
//编解码器
pipeline.addLast(new MyByteToLongDecoder());
pipeline.addLast(new MyLongToBytebufEncoder());
//自定义处理器
pipeline.addLast("mySocketServerHandler", new MySocketServerHandler());
}
}
package com.leolee.netty.handler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import java.util.UUID;
/**
* @ClassName MySocketServerHandler
* @Description: websocket服务端自定义处理器
* @Author LeoLee
* @Date 2020/8/23
* @Version V1.0
**/
public class MySocketServerHandler extends SimpleChannelInboundHandler<Long> {
@Override
protected void channelRead0(ChannelHandlerContext ctx, Long msg) throws Exception {
System.out.println(ctx.channel().remoteAddress() + ":" + msg);
ctx.writeAndFlush(654321L);
}
/**
* 功能描述: <br> 重写异常时的处理回调方法
* 〈〉在这里遇到异常直接关闭掉链接
* @Param: [ctx, cause]
* @Return: void
* @Author: LeoLee
* @Date: 2020/8/23 15:04
*/
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
cause.printStackTrace();
ctx.close();
}
}
运行结果
首先启动服务端,其次是客户端
服务端输出:
decode invoked
readable bytes num:8
/127.0.0.1:50690:123456
encode invoked
msg:654321
客户端输出:
encode invoked
msg:123456
decode invoked
readable bytes num:8
localhost/127.0.0.1:8899
Client output:654321