package com.jsowell.netty.server.yunkuaichong;

import com.jsowell.common.constant.Constants;
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;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.CommandLineRunner;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.net.InetSocketAddress;

@Slf4j
@Component
public class NettyServer implements CommandLineRunner {
	@Resource
	private NettyServerChannelInitializer nettyServerChannelInitializer;

	@Order(value = 1)
	@Override
	public void run(String... args) throws Exception {
		InetSocketAddress address = new InetSocketAddress(Constants.SOCKET_IP, Constants.SOCKET_PORT);
		this.start(address);
	}

	public void start(InetSocketAddress address) {
		// log.info("========NettyServer.start  order 1");
		//配置服务端的NIO线程组
		EventLoopGroup bossGroup = new NioEventLoopGroup(1);
		EventLoopGroup workerGroup = new NioEventLoopGroup();

		try {
			ServerBootstrap bootstrap = new ServerBootstrap() // //启动NIO服务的辅助启动类
					.group(bossGroup, workerGroup)  // 绑定线程池
					.channel(NioServerSocketChannel.class) // 启动服务时, 通过反射创建一个NioServerSocketChannel对象

					/*
					 ===> 服务器初始化时执行, 属于AbstracBootstrap的方法
					 */
					.handler(new LoggingHandler(LogLevel.DEBUG)) // handler在初始化时就会执行，可以设置打印日志级别
					// 设置tcp缓冲区, 可连接队列大小
					.option(ChannelOption.SO_BACKLOG, 128)  //服务端接受连接的队列长度，如果队列已满，客户端连接将被拒绝
					.option(ChannelOption.SO_REUSEADDR, true)  //允许重复使用本地地址和端口

					/*
					 ===> 客户端连接成功之后执行, 属于ServerBootstrap的方法,继承自AbstractBootstrap
					 */
					.childOption(ChannelOption.SO_KEEPALIVE, true)    //两小时没有数据通信时, 启用心跳保活机制探测客户端是否连接有效
					.childOption(ChannelOption.SO_REUSEADDR, true)
					.childHandler(nettyServerChannelInitializer)//编码解码

					// 地址
					.localAddress(address);

			// 绑定端口，开始接收进来的连接
			ChannelFuture future = bootstrap.bind(address.getPort()).sync();
			if (future.isSuccess()) {
				log.info("NettyServer启动成功, 开始监听端口:{}", address.getPort());
			} else {
				log.error("NettyServer启动失败", future.cause());
			}
			//关闭channel和块，直到它被关闭
			future.channel().closeFuture().sync();
		} catch (Exception e) {
			log.error("NettyServer.start error", e);
			bossGroup.shutdownGracefully();
			workerGroup.shutdownGracefully();
		}
	}
}
