netty tcp空闲设置

mac2024-05-19  30

1.场景

         正常的软硬件接口中(网络通信过程中),一般为了保证硬件和服务器连接一直在线,一般都是客户端主动发心跳到服务器,服务器根据收到心跳时间,判断间隔一段时间内,没有收到数据后,就断开连接。而本博客为什么要讲netty tcp空闲设置,这是因为用户很长时间不操作,则我们可以认为这个账号是僵尸账号,可以T下线,从而减少服务器的开销。而类似于王者荣耀,LOL等游戏都是这样处理的。

2.代码

package com.cloudtech.server; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.cloudtech.util.Consts; import io.netty.bootstrap.ServerBootstrap; import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelInitializer; import io.netty.channel.ChannelOption; import io.netty.channel.EventLoopGroup; import io.netty.channel.nio.NioEventLoopGroup; import io.netty.channel.socket.SocketChannel; import io.netty.channel.socket.nio.NioServerSocketChannel; import io.netty.handler.codec.DelimiterBasedFrameDecoder; import io.netty.handler.codec.string.StringDecoder; import io.netty.handler.timeout.IdleStateHandler; /** * * @ClassName: Server * @Description:netty服务端 * @author wude * @date 2018年9月7日 * */ public class Server { protected static final Logger logger = LoggerFactory.getLogger(Server.class); /** * 启动 */ public void start(int port) { // 服务类 ServerBootstrap b = new ServerBootstrap(); // 创建boss和worker EventLoopGroup bossGroup = new NioEventLoopGroup(); EventLoopGroup workerGroup = new NioEventLoopGroup(); //业务线程池,实现消息串行化 EventLoopGroup busyGroup = new NioEventLoopGroup(); try { // 设置循环线程组事例 b.group(bossGroup, workerGroup); // 设置channel工厂 b.channel(NioServerSocketChannel.class); // 设置管道 b.childHandler(new ChannelInitializer<SocketChannel>() { @Override public void initChannel(SocketChannel ch) throws Exception { //第一个参数 读超时 //第二个参数 写超时 //第三个参数读写超时 ch.pipeline().addLast(new IdleStateHandler(5, 5, 120)); ByteBuf delimiter = Unpooled.copiedBuffer("\r\n".getBytes()); ch.pipeline().addLast(new DelimiterBasedFrameDecoder(Consts.MAX_BUFF_LEN,delimiter)); ch.pipeline().addLast(new StringDecoder()); ch.pipeline().addLast(busyGroup,new ServerHandler()); } }); b.option(ChannelOption.SO_BACKLOG, 2048);//serverSocketchannel的设置,链接缓冲池的大小 b.childOption(ChannelOption.SO_KEEPALIVE, true);//socketchannel的设置,维持链接的活跃,清除死链接 b.childOption(ChannelOption.TCP_NODELAY, true);//socketchannel的设置,关闭延迟发送 logger.info("启动AWS420采集成功!port={}",port); //绑定端口 ChannelFuture future = b.bind(port); //等待服务端关闭 future.channel().closeFuture().sync(); } catch (Exception e) { e.printStackTrace(); } finally{ //释放资源 bossGroup.shutdownGracefully(); workerGroup.shutdownGracefully(); busyGroup.shutdownGracefully(); } } }

 注意这三个参数很重要

package com.cloudtech.server; import java.net.InetSocketAddress; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.cloudtech.server.comm.ModuleId; import com.cloudtech.server.scanner.Invoker; import com.cloudtech.server.scanner.InvokerHoler; import com.cloudtech.server.session.Session; import com.cloudtech.server.session.SessionImpl; import com.cloudtech.web.dubbo.BaseDataResult; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.SimpleChannelInboundHandler; /** * * @ClassName: SessionManager * @Description: 消息接受处理类 * @author wude * @date 2018年9月7日 */ public class ServerHandler extends SimpleChannelInboundHandler<String> { private static Logger LOGGER = LoggerFactory.getLogger(ServerHandler.class); @SuppressWarnings("unused") @Override protected void messageReceived(ChannelHandlerContext ctx, String msg) throws Exception { try { Session session = new SessionImpl(ctx.channel()); InetSocketAddress insocket = (InetSocketAddress) ctx.channel().remoteAddress(); String ip = insocket.getAddress().getHostAddress(); int port = insocket.getPort(); // 获取命令执行器 Invoker invoker = InvokerHoler.getInvoker(ModuleId.UPLOAD_DATA); if (invoker != null) { BaseDataResult dataResult = (BaseDataResult) invoker.invoke(session, msg, ip, port); } else { return; } } catch (Exception e) { LOGGER.debug("AWS420 解析代码失败ip:{} port:{} result:{}", e.getMessage()); } } @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { LOGGER.debug("进入异常方法:", cause.getMessage()); ctx.close(); } @Override public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception { if (evt instanceof IdleStateEvent) { if (((IdleStateEvent) evt).state().equals(IdleState.READER_IDLE)) { LOGGER.error("Read Idle"); ctx.close(); } } else { super.userEventTriggered(ctx, evt); } } }

按上面的配置,5,5,120    假设客户端5秒内没有与服务器进行任何的数据交互,我们则可以认为该账号是僵尸账号,直接强制T下线

程序猿学社 认证博客专家 Java Spring Boot 公众号程序猿学社,关注社长,跟着社长一起学习github文章系统整理https://github.com/ITfqyd/cxyxs
最新回复(0)