Netty-主动关闭Server

最近在实现某个功能需求的时候使用到了Netty,我们的需求需要netty的server端能实现主动关闭,而之前一直没有深入了解过,只会实现简单的连接,看了一些资料并实验后找到关闭server的方法:

1.主动关闭server

  • 如下面的代码所示,这里启动server时将ServerChannel的实例保存至静态属性,然后暴露一个closeServer()方法,直接调用ServerChannel的close()方法,此线程则会从阻塞状态恢复,向下执行,进入finally代码块,退出server并清理相关资源。
package com.huoli.mj.rpc.server;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.Channel;
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 org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class NettyServer {

	private static Logger logger = LoggerFactory.getLogger(NettyServer.class);
	
	private static Channel serverChannel;

	/** 关闭当前server */
	public static void closeServer() {
		if (serverChannel != null) {
			logger.info("close server");
			serverChannel.close();
			serverChannel = null;
		}
	}

	/** 启动server */
	private void start(int port) throws Exception {
		// 通过独立线程启动server
		Thread nettyServer = new Thread(() -> {
			EventLoopGroup bossGroup = new NioEventLoopGroup();
			EventLoopGroup workerGroup = new NioEventLoopGroup();
			try {
				ServerBootstrap b = new ServerBootstrap();
				b.group(bossGroup, workerGroup).channel(NioServerSocketChannel.class)
						.childHandler(new DefaultServerChannelInitializer()).option(ChannelOption.SO_BACKLOG, 128)
						.childOption(ChannelOption.SO_KEEPALIVE, true);

				// 绑定端口启动成功
				ChannelFuture channelFuture = b.bind(port).sync();
				logger.info("start rpc server success with port:" + port);
				// 将ServerChannel保存下来
				serverChannel = channelFuture.channel();
				// 阻塞至channel关闭
				serverChannel.closeFuture().sync();
			} catch (Exception e) {
				logger.error("MJ netty server error", e);
			} finally {
				// 优雅关闭server线程及相关资源
				workerGroup.shutdownGracefully();
				bossGroup.shutdownGracefully();
			}
		});
		nettyServer.setName("netty-server-thread");
		nettyServer.setDaemon(true);
		nettyServer.start();
	}
}

 

2.在ChannelHandler中关闭server

  • 在某些场景下,会需要在特定事件回调时主动关闭server端。下面代码展示了在监听到异常抛出事件时主动关闭server的操作。
  • 以下面代码为例:ChannelHandler的事件回调方法中,ctx.close() 或 ctx.channel().close()只能关闭与某个客户端连接的channel,而ctx.channel().parent()获取到的是ServerChannel,所以调用ctx.channel().parent().close()才能关闭server,使server启动线程从阻塞状态恢复,从而在finally代码块中执行退出关闭操作。
package top.kylewang.netty.demo.second.server;

import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class TestSocketServerHandler extends ChannelInboundHandlerAdapter {

	private static final Logger logger = LoggerFactory.getLogger(TestSocketServerHandler.class);

	/** 监听异常 */
	@Override
	public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
		cause.printStackTrace();
		// 关闭serverChannel
		ctx.channel().parent().close();
	}
}

 

### Netty TCP Server 实现心跳包机制 在 Netty 的 TCP Server 中实现心跳包机制可以通过 `IdleStateHandler` 来完成。此工具可以监控通道的状态,当某个连接处于空闲状态超过指定时间时触发事件通知[^3]。 以下是基于 Netty 的 TCP Server 和 Client 心跳包机制的完整示例: #### 服务端代码 ```java import io.netty.bootstrap.ServerBootstrap; import io.netty.channel.*; import io.netty.channel.nio.NioEventLoopGroup; import io.netty.channel.socket.SocketChannel; import io.netty.channel.socket.nio.NioServerSocketChannel; import io.netty.handler.timeout.IdleStateEvent; import io.netty.handler.timeout.IdleStateHandler; public class HeartbeatServer { public static void main(String[] args) throws Exception { EventLoopGroup bossGroup = new NioEventLoopGroup(); EventLoopGroup workerGroup = new NioEventLoopGroup(); try { ServerBootstrap b = new ServerBootstrap(); b.group(bossGroup, workerGroup) .channel(NioServerSocketChannel.class) .childHandler(new ChannelInitializer<SocketChannel>() { @Override protected void initChannel(SocketChannel ch) throws Exception { ChannelPipeline p = ch.pipeline(); // 设置读写空闲时间为10秒 p.addLast(new IdleStateHandler(0, 0, 10)); p.addLast(new HeartbeatServerHandler()); } }); ChannelFuture f = b.bind(8080).sync(); // 绑定到端口8080 System.out.println("服务器已启动..."); f.channel().closeFuture().sync(); } finally { workerGroup.shutdownGracefully(); bossGroup.shutdownGracefully(); } } static class HeartbeatServerHandler extends SimpleChannelInboundHandler<String> { @Override protected void channelRead0(ChannelHandlerContext ctx, String msg) throws Exception { if ("heartbeat".equals(msg)) { // 接收心跳消息 System.out.println("接收到客户端的心跳:" + msg); ctx.writeAndFlush("ack"); // 返回确认响应 } else { System.out.println("接收到来自客户端的消息:" + msg); } } @Override public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception { if (evt instanceof IdleStateEvent) { // 如果发生空闲事件 System.out.println("未接收到客户端的心跳,关闭连接"); ctx.close(); // 关闭连接 } } } } ``` #### 客户端代码 ```java import io.netty.bootstrap.Bootstrap; import io.netty.channel.*; import io.netty.channel.nio.NioEventLoopGroup; import io.netty.channel.socket.SocketChannel; import io.netty.channel.socket.nio.NioSocketChannel; import io.netty.handler.timeout.IdleStateHandler; public class HeartbeatClient { public static void main(String[] args) throws Exception { EventLoopGroup group = new NioEventLoopGroup(); Bootstrap bootstrap = new Bootstrap(); bootstrap.group(group) .channel(NioSocketChannel.class) .handler(new ChannelInitializer<SocketChannel>() { @Override protected void initChannel(SocketChannel ch) throws Exception { ChannelPipeline pipeline = ch.pipeline(); // 设置写空闲时间为5秒 pipeline.addLast(new IdleStateHandler(0, 0, 5)); pipeline.addLast(new HeartbeatClientHandler()); } }); ChannelFuture future = bootstrap.connect("localhost", 8080).sync(); future.channel().closeFuture().sync(); } static class HeartbeatClientHandler extends SimpleChannelInboundHandler<String> { private int heartbeatCount = 0; @Override public void channelActive(ChannelHandlerContext ctx) throws Exception { super.channelActive(ctx); sendHeartbeat(ctx); // 连接建立后立即发送心跳 } @Override protected void channelRead0(ChannelHandlerContext ctx, String msg) throws Exception { if ("ack".equals(msg)) { // 收到服务器返回的确认消息 System.out.println("接收到服务器的心跳确认:" + msg); } } @Override public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception { if (evt instanceof IdleStateEvent) { // 发生空闲事件时重新发送心跳 sendHeartbeat(ctx); } } private void sendHeartbeat(ChannelHandlerContext ctx) { ctx.writeAndFlush("heartbeat"); // 向服务器发送心跳消息 System.out.println("发送第 " + (++heartbeatCount) + " 次心跳"); } } } ``` 上述代码展示了如何利用 Netty 的 `IdleStateHandler` 实现心跳机制。服务端会在一定时间内未接收到任何数据(包括心跳)时主动断开连接[^4]。而客户端会定期向服务端发送心跳包以维持连接活动状态。 --- ###
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值