Channel的实现方式

762 阅读3分钟

这是我参与更文挑战的第 14 天,活动详情查看

开发者需要做什么

开发者需要实现channelHandler具体实现类

  • ChannelInboundHandler处理IO事件
  • ChannelOutboundHandler处理IO操作

更方便的方式:实现对应的适配器。

  • ChannelInboundHandlerAdapter处理IO事件
  • ChannelOutboundHandlerAdapter处理IO操作
  • ChannelDuplexHandler处理IO事件与操作

子类实现ChannelInboundHandlerAdapter
注意:

  • ChannelInboundHandlerAdapter 是提供了一种实现而已,子类如果要继承,需要覆盖父类中的方法,并且不需要调用super.xxxxMethod();
  • 在channelRead(ChannelHandlerContext, Object)方法自动返回后不会发布消息。 如果您正在寻找ChannelInboundHandler自动发布接收消息的实施方式,请参阅impleChannelInboundHandler
方法解释
channelRegisteredChannel已经注册到一个EventLoop上
channelUnregisteredChannel已创建,还未注册到一个EventLoop上
channelActiveChannel是活跃状态(连接到某个远端),可以收发数据;将会在连接被建立并且准备进行通信时被调用
channelInactivechannel 不活跃,断开连接
channelReadCompletechannel读取完毕事件
userEventTriggeredchannel 用户事件触发
channelWritabilityChangedchannel 可写更改
exceptionCaughtchannel 捕获到异常
handlerAddedchannel 助手类(拦截器)的添加
handlerRemovedchannel 助手类(拦截器)移除
channelReadchannel读取数据;是在数据被接收的时候调用

一个Channel 正常的生命周期如下图所示。随着状态发生变化相应的事件产生。这些事件被转发到ChannelPipeline中的ChannelHandler 来触发相应的操作。

image.png

ChannelHandlerContext

接口 ChannelHandlerContext 代表 ChannelHandler 和ChannelPipeline 之间的关联,并在 ChannelHandler 添加到 ChannelPipeline 时创建一个实例。ChannelHandlerContext 的主要功能是管理通过同一个 ChannelPipeline 关联的 ChannelHandler 之间的交互。

ChannelHandlerContext 有许多方法,其中一些也出现在 Channel 和ChannelPipeline 本身。然而,如果您通过Channel 或ChannelPipeline 的实例来调用这些方法,他们就会在整个 pipeline中传播 。相比之下,一样的 的方法在 ChannelHandlerContext的实例上调用, 就只会从当前的 ChannelHandler 开始并传播到相关管道中的下一个有处理事件能力的 ChannelHandler 。

ChannelHandlerContext API 总结如下

方法名称描述
bind请求绑定到给定的SocketAddress并返回ChannelFuture
channel返回绑定到此实例的通道
close请求关闭通道并返回一个ChannelFuture
connect请求连接到给定的SocketAddress并返回ChannelFuture
deregister请求从先前分配的EventExecutor注销并返回ChannelFuture
disconnect请求断开与远程对等机的连接并返回通道
executor返回发送事件的EventExecutor
fireChannelActive通道处于活动状态(已连接)
fireChannelInactive通道不活动(关闭)
fireChannelRead通道接收到消息
fireChannelReadComplete将channelWritabilityChanged事件触发到下一个事件

ChannelInboundHandler。

  • handler |返回绑定到此实例的ChannelHandler。
  • isRemoved如果关联的ChannelHandler已从ChannelPipeline中删除,则返回true。
  • name |返回此实例的唯一名称。
  • pipeline |返回关联的ChannelPipeline。
  • read |请求将数据从通道读入第一个入站缓冲区。如果成功,则触发channelRead事件并通知处理程序channelReadComplete。
  • write |请求通过管道通过此实例写入消息。

其他注意注意事项:

  • ChannelHandlerContext 与 ChannelHandler 的关联从不改变,所以缓存它的引用是安全的。
  • 正如我们前面指出的,ChannelHandlerContext 所包含的事件流比其他类中同样的方法都要短,利用这一点可以尽可能高地提高性能
public class HeartBeatHandler extends ChannelInboundHandlerAdapter{
    public void channelActive(final ChannelHandlerContext ctx) throws Exception {
        //链接成功处理
    }
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        if(msg instanceof TextWebSocketFrame) {
            //业务处理
        }else {
            ctx.fireChannelRead(msg);
        }
    }
}

创建服务端、客户端

@PostConstruct
	public void init() {
		final ServerBootstrap serverBootstrap=new ServerBootstrap();
		LOGGER.info("init server in port:[{}]",socketPort);
		serverBootstrap.group(bossGroup, workerGroup)
			.channel(NioServerSocketChannel.class)
	        .option(ChannelOption.SO_BACKLOG, 1024)
	        .option(ChannelOption.TCP_NODELAY, true)
	        .childOption(ChannelOption.SO_KEEPALIVE, true)
	        .childOption(ChannelOption.RCVBUF_ALLOCATOR, new FixedRecvByteBufAllocator(204800))
	        .childHandler(new ChannelInitializer<SocketChannel>() {
				@Override
				protected void initChannel(SocketChannel ch) throws Exception {
					ch.pipeline().addLast("http-codec", new HttpServerCodec());
			        ch.pipeline().addLast("aggregator", new HttpObjectAggregator(204800));
			        ch.pipeline().addLast("http-chunked", new ChunkedWriteHandler());
                                //后面是自己业务方法类,都extends ChannelInboundHandlerAdapter{
			        ch.pipeline().addLast("handShakerHandler", handShakerHandler);
			        ch.pipeline().addLast("textMessageHandler", textMessageHandler);
			        ch.pipeline().addLast("binaryMessageHandler", binaryMessageHandler);
			        ch.pipeline().addLast("heartBeatHandler", heartBeatHandler);
				}
	        });
		serverBootstrap.bind(socketPort);
	}
	
	@PreDestroy
	public void destory() {
		try {
			bossGroup.shutdownGracefully().sync();
			workerGroup.shutdownGracefully().sync();
		} catch (InterruptedException e) {
			LOGGER.error("error in destory netty work group failed");
		}
	}