netty tcp空闲设置

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下线

发布了202 篇原创文章 · 获赞 363 · 访问量 15万+

猜你喜欢

转载自blog.csdn.net/qq_16855077/article/details/102839231