package com.mvc.netty;


import com.mvc.task.SocketProperties;
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.logging.LogLevel;
import io.netty.handler.logging.LoggingHandler;
import io.netty.handler.timeout.IdleStateHandler;
import io.netty.handler.timeout.ReadTimeoutHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.util.concurrent.TimeUnit;

/**
 * 水表Netty服务
 */
@Component
public class WmNettyServer implements CommandLineRunner {

    private static Logger log = LoggerFactory.getLogger(WmNettyServer.class);
    @Resource
    private SocketProperties properties;

    @Resource
    private WmNettyServerHandler_2 nettyServerHandler;

    /**
     * 服务入口
     *
     * @param args
     * @throws Exception
     */
    @Override
    public void run(String... args) throws Exception {

        //定义主线程组，用于接收客户端请求，但是并不做任何逻辑处理
        EventLoopGroup acceptorGroup = new NioEventLoopGroup();
        //从线程组，主线程组会把相应的请求转交给该线程组，由从线程组去做任务
        EventLoopGroup nioGroup = new NioEventLoopGroup();
        try {
            //创建netty服务器
            ServerBootstrap bootstrap = new ServerBootstrap();
            //服务器设置绑定两个线程组，并设置相应的助手类【handler】
            bootstrap.group(acceptorGroup, nioGroup)
                    .channel(NioServerSocketChannel.class)//设置nio双向通道
                    .childOption(ChannelOption.TCP_NODELAY, true)   //通过NoDelay禁用Nagle,使消息立即发出去，不用等待到一定的数据量才发出去
                    .childOption(ChannelOption.SO_KEEPALIVE, true)  //保持长连接状态
                    .childOption(ChannelOption.SO_REUSEADDR, true)
                    .childHandler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        protected void initChannel(SocketChannel socketChannel) throws Exception {
                            socketChannel.pipeline().addLast(nettyServerHandler);
                            socketChannel.pipeline().addLast(new LoggingHandler(LogLevel.INFO));

                            //加入连接时长保持限制(防止终端长时间未断开) 2022-12-30 guozhao
                            //5分钟未读取到数据，直接断开连接(改为20分钟,NB OTA时可能耗时较长 2024-07-08 guozhao)
                            socketChannel.pipeline().addLast(new ReadTimeoutHandler(1200));
                            ////socketChannel.pipeline().addLast(new WriteTimeoutHandler(30));

                            socketChannel.pipeline().addLast(new IdleStateHandler(0, 0, 1, TimeUnit.MINUTES)); // 设置读、写、读写空闲时间
                            socketChannel.pipeline().addLast(new MyIdleStateHandler());
                        }
                    });
            Channel channel = bootstrap.bind(properties.getPort()).sync().channel();
            channel.closeFuture().sync();

//            ChannelFuture future1 = bootstrap.bind(properties.getPort());
//            ChannelFuture future2 = bootstrap.bind(properties.getPort() + 1);
//
//            future1.channel().closeFuture().addListeners((ChannelFutureListener) channelFuture -> channelFuture.channel().close());
//            future2.channel().closeFuture().addListeners((ChannelFutureListener) channelFuture -> channelFuture.channel().close());

        } catch (Exception e) {
            log.error("Netty服务创建失败！", e);
        } finally {
            //监听多个端口时,上面没有阻塞了,不注释会直接关闭
            acceptorGroup.shutdownGracefully();
            nioGroup.shutdownGracefully();
        }
    }
}
