|
@@ -0,0 +1,108 @@
|
|
|
+package com.its.vds.xnettcp.center;
|
|
|
+
|
|
|
+import com.its.app.utils.NettyUtils;
|
|
|
+import com.its.app.utils.OS;
|
|
|
+import com.its.vds.config.CenterCommConfig;
|
|
|
+import com.its.vds.xnettcp.center.codec.CenterTcpServerEncoder;
|
|
|
+import com.its.vds.xnettcp.center.handler.CenterTcpServerInboundHandler;
|
|
|
+import com.its.vds.xnettcp.center.initializer.CenterTcpServerInitializer;
|
|
|
+import io.netty.bootstrap.ServerBootstrap;
|
|
|
+import io.netty.channel.ChannelFuture;
|
|
|
+import io.netty.channel.ChannelHandler;
|
|
|
+import io.netty.channel.ChannelOption;
|
|
|
+import io.netty.channel.EventLoopGroup;
|
|
|
+import io.netty.channel.epoll.Epoll;
|
|
|
+import lombok.RequiredArgsConstructor;
|
|
|
+import lombok.extern.slf4j.Slf4j;
|
|
|
+import org.springframework.stereotype.Service;
|
|
|
+
|
|
|
+@Slf4j
|
|
|
+@RequiredArgsConstructor
|
|
|
+@Service
|
|
|
+public class CenterTcpServerService {
|
|
|
+
|
|
|
+ private final CenterCommConfig config;
|
|
|
+ private final CenterTcpServerInboundHandler centerTcpServerInboundHandler;
|
|
|
+ private final CenterTcpServerEncoder centerTcpServerEncoder;
|
|
|
+
|
|
|
+ private EventLoopGroup acceptGroups = null;
|
|
|
+ private EventLoopGroup workerGroups = null;
|
|
|
+ private ServerBootstrap serverBootstrap = null;
|
|
|
+ private ChannelFuture channelFuture = null;
|
|
|
+
|
|
|
+ public void run() {
|
|
|
+ if (!OS.isWindows()) {
|
|
|
+ if (!Epoll.isAvailable()) {
|
|
|
+ Epoll.unavailabilityCause().printStackTrace();
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if (NettyUtils.isEpollAvailable()) {
|
|
|
+ log.info("서버가 리눅스 EPOLL 모드에서 실행됩니다.");
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ log.info("서버가 윈도우 NIO 모드에서 실행됩니다.");
|
|
|
+ }
|
|
|
+
|
|
|
+ this.serverBootstrap = createBootstrap();
|
|
|
+
|
|
|
+ log.info("************************************************************************************");
|
|
|
+ log.info("** Center Communication Server Information **");
|
|
|
+ log.info("** bindAddress: {}", this.config.getBindingAddr());
|
|
|
+ log.info("** listenPort: {}", this.config.getListenPort());
|
|
|
+ log.info("** backlog: {}", this.config.getBacklog());
|
|
|
+ log.info("** acceptThreads: {}", this.config.getAcceptThreads());
|
|
|
+ log.info("** workerThreads: {}", this.config.getWorkerThreads());
|
|
|
+ log.info("************************************************************************************");
|
|
|
+
|
|
|
+ try {
|
|
|
+ if (this.config.getBindingAddr().equals("0.0.0.0")) {
|
|
|
+ this.channelFuture = this.serverBootstrap.bind(this.config.getListenPort());
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ this.channelFuture = this.serverBootstrap.bind(this.config.getBindingAddr(), config.getListenPort());
|
|
|
+ }
|
|
|
+ }
|
|
|
+ catch (Exception e) {
|
|
|
+ log.error("start, InterruptedException");
|
|
|
+ this.acceptGroups.shutdownGracefully();
|
|
|
+ this.workerGroups.shutdownGracefully();
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ public void stop() {
|
|
|
+ try {
|
|
|
+ this.acceptGroups.shutdownGracefully().sync();
|
|
|
+ this.workerGroups.shutdownGracefully().sync();
|
|
|
+ this.channelFuture.channel().closeFuture().sync();
|
|
|
+ } catch (InterruptedException e) {
|
|
|
+ log.error("stop, InterruptedException");
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ public ServerBootstrap createBootstrap() {
|
|
|
+ final int DEFAULT_EVENT_THREADS = Runtime.getRuntime().availableProcessors() * 2;
|
|
|
+ ServerBootstrap serverBootstrap = new ServerBootstrap();
|
|
|
+ EventLoopGroup acceptGroups;
|
|
|
+ EventLoopGroup workerGroups;
|
|
|
+
|
|
|
+ acceptGroups = NettyUtils.newEventLoopGroup(config.getAcceptThreads(), "Accept");
|
|
|
+ workerGroups = NettyUtils.newEventLoopGroup(config.getWorkerThreads(), "Worker");
|
|
|
+ serverBootstrap.channel(NettyUtils.getServerSocketChannel());
|
|
|
+ serverBootstrap.group(acceptGroups, workerGroups);
|
|
|
+
|
|
|
+ serverBootstrap.option(ChannelOption.AUTO_READ, true);
|
|
|
+ serverBootstrap.option(ChannelOption.SO_BACKLOG, config.getBacklog());
|
|
|
+ serverBootstrap.option(ChannelOption.SO_RCVBUF, config.getRcvBuf());
|
|
|
+ serverBootstrap.option(ChannelOption.SO_REUSEADDR, true);
|
|
|
+ serverBootstrap.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, config.getConnectTimeoutSeconds()*1000);
|
|
|
+
|
|
|
+ serverBootstrap.childOption(ChannelOption.SO_LINGER, 0); // 4way-handshake 비활성
|
|
|
+ serverBootstrap.childOption(ChannelOption.SO_KEEPALIVE, false); // KEEPALIVE 비활성(활성: true)
|
|
|
+ serverBootstrap.childOption(ChannelOption.SO_REUSEADDR, true); // 소켓 재사용
|
|
|
+ serverBootstrap.childOption(ChannelOption.TCP_NODELAY, true); // Nagle 알고리즘 비활성화
|
|
|
+ serverBootstrap.childHandler((ChannelHandler)new CenterTcpServerInitializer(this.centerTcpServerInboundHandler, this.centerTcpServerEncoder));
|
|
|
+
|
|
|
+ return serverBootstrap;
|
|
|
+ }
|
|
|
+
|
|
|
+}
|