Netty:ChannelHandler抛出异常,对应的channel被关闭

文章讲述了使用Netty构建的Socket服务端,当ChannelHandler在处理客户端数据时抛出未捕获的异常,会导致对应channel关闭,但不影响其他客户端连接。示例展示了服务端与客户端的交互及其异常处理机制。
摘要由CSDN通过智能技术生成

说明

使用Netty框架构建的socket服务端在处理客户端请求时,每接到一个客户端的连接请求,服务端会分配一个channel处理跟该客户端的交互。如果处理该channel数据的ChannelHandler抛出异常没有捕获,那么该channel会关闭。但服务端和其它客户端的通信不受影响。

代码示例

该示例验证场景:
服务端和客户端建立了正常的连接,服务端给该客户端分配了一个channel。从该channel读取数据的ChannelHandler抛出异常,没有被捕获,导致该channel被关闭,服务端和该客户端的通信中断。但服务端和其它客户端的通信不受影响。

服务端代码片段

package com.thb.power.server;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.logging.LogLevel;
import io.netty.handler.logging.LoggingHandler;

/**
 * 服务端的主函数
 * @author thb
 *
 */
public class MainStation {
	private static final Logger logger = LogManager.getLogger();
	
	static final int PORT = Integer.parseInt(System.getProperty("port", "22335"));

	public static void main(String[] args) throws Exception {
		logger.traceEntry();
		
		// 配置服务器
		EventLoopGroup bossGroup = new NioEventLoopGroup();
		EventLoopGroup workerGroup = new NioEventLoopGroup();
		try {
			ServerBootstrap b =  new ServerBootstrap();
			b.group(bossGroup, workerGroup)
			 .channel(NioServerSocketChannel.class)
			 .option(ChannelOption.SO_BACKLOG, 100)
			 .handler(new LoggingHandler(LogLevel.INFO))
			 .childHandler(new MainStationInitializer());
			
			// 启动服务端
			ChannelFuture f = b.bind(PORT).sync();
			
			// 等待直到server socket关闭
			f.channel().closeFuture().sync();
		} finally {
			// 关闭所有event loops以便终止所有的线程
			bossGroup.shutdownGracefully();
			workerGroup.shutdownGracefully();
		}
		
		logger.traceExit();
	}

}
package com.thb.power.server;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import com.thb.power.constant.PowerConstants;
import com.thb.power.handler.VerifyChecksumHandler;

import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.socket.SocketChannel;
import io.netty.handler.codec.DelimiterBasedFrameDecoder;
import io.netty.handler.logging.LogLevel;
import io.netty.handler.logging.LoggingHandler;

public class MainStationInitializer extends ChannelInitializer<SocketChannel> {
	private static final Logger logger = LogManager.getLogger();

	 @Override
	 public void initChannel(SocketChannel ch) throws Exception {
		 logger.traceEntry();
		 
		 ChannelPipeline p = ch.pipeline();		 
		
		 p.addLast(new LoggingHandler(LogLevel.INFO));
		
		 // 用报文开头的字符、结尾的字符作为分隔符,解析成完整的报文	
		 p.addLast(new DelimiterBasedFrameDecoder(PowerConstants.SOCKET_MAX_BYTES_PER_PACKET, true, 
				 Unpooled.wrappedBuffer(new byte[] {PowerConstants.SOCKET_END_MARK}),
				 Unpooled.wrappedBuffer(new byte[] {PowerConstants.SOCKET_START_MARK})));	

		 p.addLast(new VerifyChecksumHandler());
		 
		 logger.traceExit();
	 }
}
package com.thb.power.handler;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufUtil;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.handler.codec.CorruptedFrameException;
import io.netty.util.ReferenceCounted;

/**
 * 验证接收的报文的校验和
 * @author thb
 *
 */
public class VerifyChecksumHandler extends ChannelInboundHandlerAdapter {
	
	private static final Logger logger = LogManager.getLogger();
	
	@Override
	public void channelRead(ChannelHandlerContext ctx, Object msg) {
		logger.traceEntry();		
		
		if (msg instanceof ByteBuf) {
			ByteBuf m = (ByteBuf)msg;
			
			logger.info("msg reference count: {}", m.refCnt());
			// 此处人为构造场景,以便验证抛出异常的情况。将引用计数减少1,执行后,引用计数变为0,读数据会抛异常
			m.release();
			m.readByte();
			
			logger.info("readableBytes: " + m.readableBytes());		
			
			// 将收到的报文的每个字节转换为十六进制打印出来			
			logger.info(ByteBufUtil.prettyHexDump(m));			
			

		} else {
			// 这个数据不需要向后传递,因为数据已经错误了
			// 传入的对象msg如果实现了ReferenceCounted接口,那么显式就释放
			if (msg instanceof ReferenceCounted) {
				((ReferenceCounted)msg).release();
			}
			
			throw new CorruptedFrameException("received illegal data: not ByteBuf type");
		}
		
		logger.traceExit();
	}
	
	@Override
	public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
		cause.printStackTrace();
		ctx.close();
	}
}
package com.thb.power.constant;

/**
 * 保存了一些常数
 * @author thb
 *
 */
public final class PowerConstants {

	/**
	 * 定义了私有构造函数,防止被外部实例化
	 */
	private PowerConstants() {
		
	}
	
	/**
	 * 通信报文的起始符
	 */
	public static final byte SOCKET_START_MARK = (byte)0x68;
	
	/**
	 * 通信报文的的结束符
	 */
	public static final byte SOCKET_END_MARK = (byte)0x16;
	
	/**
	 * 每个报文的最大字节数
	 */
	public static final short SOCKET_MAX_BYTES_PER_PACKET = 1024;
}

启动服务端

在这里插入图片描述

启动客户端,并向服务端发送数据

在这里插入图片描述
在这里插入图片描述

服务端的ChannelHandler处理数据时抛出了异常,断开了和该客户端的连接

在这里插入图片描述
在这里插入图片描述
从输出可以看出,服务端处理客户端channel数据的ChannelHandler抛出了异常,服务端终止了和该客户端的连接。

观察客户端的输出

在这里插入图片描述
从输出可以看出,客户端终止了和服务端的连接。

服务端可以继续接受新的客户端连接

在这里插入图片描述
从输出可以看出,服务端可以正常接受新的客户端的连接

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值