[JAVA] Spring Boot - Netty 서버 만들기

Category: JAVA | March 11, 2017

개요

차기 프로젝트에서 모뎀 단말기와 통신 하는 모듈을 개발 하기 위해 Spring Boot + Netty로 프로그램을 구성할 수 있는지 테스트.

프로젝트 구조

메이븐 설정



    4.0.0

    com.example
    demo
    0.0.1-SNAPSHOT
    jar

    boot-netty
    Demo project for Spring Boot

    
        org.springframework.boot
        spring-boot-starter-parent
        1.5.2.RELEASE
         
    

    
        UTF-8
        UTF-8
        1.8
    

    
        
            org.springframework.boot
            spring-boot-starter
        

        
            org.springframework.boot
            spring-boot-starter-test
            test
        

        
        
            io.netty
            netty-all
            4.1.8.Final
        

    

    
        
            
                org.springframework.boot
                spring-boot-maven-plugin
            
        
    



BootNettyApplication

package com.example;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ConfigurableApplicationContext;

@SpringBootApplication
public class BootNettyApplication {

    @Autowired
    private ApplicationContext context;

    public static void main(String[] args) {
        ConfigurableApplicationContext context = SpringApplication.run(BootNettyApplication.class, args);
        NettyServer nettyServer = context.getBean(NettyServer.class);
        nettyServer.start();
    }
}

NettyServer

package com.example;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
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.logging.LogLevel;
import io.netty.handler.logging.LoggingHandler;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.PropertySource;
import org.springframework.stereotype.Component;


/**
 * The type Netty server.
 */
@Component
@PropertySource(value = "classpath:/application.properties")
public class NettyServer {
    /**
     * The Tcp port.
     */
    @Value("${tcp.port}")
    private int tcpPort;

    /**
     * The Boss count.
     */
    @Value("${boss.thread.count}")
    private int bossCount;

    /**
     * The Worker count.
     */
    @Value("${worker.thread.count}")
    private int workerCount;

    /**
     * The constant SERVICE_HANDLER.
     */
    private static final ServiceHandler SERVICE_HANDLER = new ServiceHandler();

    /**
     * Start.
     */
    public void start() {
        /**
         * 클라이언트 연결을 수락하는 부모 스레드 그룹
         */
        EventLoopGroup bossGroup = new NioEventLoopGroup(bossCount);
        /**
         * 연결된 클라이언트ㄹ의 소켓으로 부터 데이터 입출력 및 이벤트를 담당하는 자식 스레드
         */
        EventLoopGroup workerGroup = new NioEventLoopGroup();

        try {
            ServerBootstrap b = new ServerBootstrap();
            b.group(bossGroup, workerGroup)
                    .channel(NioServerSocketChannel.class)                              //서버 소켓 입출력 모드를 NIO로 설정
                    .handler(new LoggingHandler(LogLevel.INFO))                         //서버 소켓 채널 핸들러 등록
                    .childHandler(new ChannelInitializer() {             //송수신 되는 데이터 가공 핸들러
                        @Override
                        protected void initChannel(SocketChannel ch) throws Exception {
                            ChannelPipeline pipeline = ch.pipeline();
                            pipeline.addLast(new LoggingHandler(LogLevel.INFO));
                            pipeline.addLast(SERVICE_HANDLER);
                        }
                    });

            ChannelFuture channelFuture = b.bind(tcpPort).sync();
            channelFuture.channel().closeFuture().sync();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            bossGroup.shutdownGracefully();
            workerGroup.shutdownGracefully();
        }
    }
}</pre>

ServiceHandler

package com.example;

import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.channel.group.ChannelGroup;
import io.netty.channel.group.DefaultChannelGroup;
import io.netty.util.concurrent.GlobalEventExecutor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.nio.charset.Charset;


/**
 * The type Service handler.
 */
@ChannelHandler.Sharable
public class ServiceHandler extends ChannelInboundHandlerAdapter {

    /**
     * The Logger.
     */
    Logger logger = LoggerFactory.getLogger(this.getClass());

    /**
     * The Channels.
     */
    private final ChannelGroup channels = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);

    /**
     * Channel active.
     *
     * @param ctx the ctx
     * @throws Exception the exception
     */
    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        channels.add(ctx.channel());
    }

    /**
     * Channel read.
     *
     * @param ctx the ctx
     * @param msg the msg
     * @throws Exception the exception
     */
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        ByteBuf byteBuf = (ByteBuf) msg;
        logger.debug("message : {} ",byteBuf.toString(Charset.defaultCharset()));
        channels.writeAndFlush(msg);
    }
}

application.properties

tcp.port=8080
boss.thread.count=1
worker.thread.count=1
so.keepalive=true
so.backlog=100

테스트

BootNettyApplication 실행

  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::        (v1.5.2.RELEASE)

2017-03-11 04:20:05.735  INFO 7356 --- [           main] com.example.BootNettyApplication         : Starting BootNettyApplication on DESKTOP-CC5LT0H with PID 7356 (C:\EEProject\workspaces_example\boot-netty\target\classes started by redpunk in C:\EEProject\workspaces_example\boot-netty)
2017-03-11 04:20:05.739  INFO 7356 --- [           main] com.example.BootNettyApplication         : No active profile set, falling back to default profiles: default
2017-03-11 04:20:05.812  INFO 7356 --- [           main] s.c.a.AnnotationConfigApplicationContext : Refreshing org.springframework.context.annotation.AnnotationConfigApplicationContext@35a50a4c: startup date [Sat Mar 11 04:20:05 KST 2017]; root of context hierarchy
2017-03-11 04:20:06.893  INFO 7356 --- [           main] o.s.j.e.a.AnnotationMBeanExporter        : Registering beans for JMX exposure on startup
2017-03-11 04:20:06.927  INFO 7356 --- [           main] com.example.BootNettyApplication         : Started BootNettyApplication in 1.635 seconds (JVM running for 2.153)
2017-03-11 04:20:08.381  INFO 7356 --- [ntLoopGroup-2-1] io.netty.handler.logging.LoggingHandler  : [id: 0x5a6c69e2] REGISTERED
2017-03-11 04:20:08.382  INFO 7356 --- [ntLoopGroup-2-1] io.netty.handler.logging.LoggingHandler  : [id: 0x5a6c69e2] BIND: 0.0.0.0/0.0.0.0:8080
2017-03-11 04:20:08.385  INFO 7356 --- [ntLoopGroup-2-1] io.netty.handler.logging.LoggingHandler  : [id: 0x5a6c69e2, L:/0:0:0:0:0:0:0:0:8080] ACTIVE

텔넷으로 메세지 전송

2017-03-11 04:26:45.313  INFO 7356 --- [ntLoopGroup-2-1] io.netty.handler.logging.LoggingHandler  : [id: 0x5a6c69e2, L:/0:0:0:0:0:0:0:0:8080] RECEIVED: [id: 0x57c00a34, L:/127.0.0.1:8080 - R:/127.0.0.1:8667]
2017-03-11 04:26:45.315  INFO 7356 --- [ntLoopGroup-3-1] io.netty.handler.logging.LoggingHandler  : [id: 0x57c00a34, L:/127.0.0.1:8080 - R:/127.0.0.1:8667] REGISTERED
2017-03-11 04:26:45.315  INFO 7356 --- [ntLoopGroup-3-1] io.netty.handler.logging.LoggingHandler  : [id: 0x57c00a34, L:/127.0.0.1:8080 - R:/127.0.0.1:8667] ACTIVE
2017-03-11 04:26:48.507  INFO 7356 --- [ntLoopGroup-3-1] io.netty.handler.logging.LoggingHandler  : [id: 0x57c00a34, L:/127.0.0.1:8080 - R:/127.0.0.1:8667] RECEIVED: 7B
         +-------------------------------------------------+
         |  0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f |
+--------+-------------------------------------------------+----------------+
|00000000| 68 65 6c 6c 6f 0d 0a                            |hello..         |
+--------+-------------------------------------------------+----------------+
2017-03-11 04:26:48.521  INFO 7356 --- [ntLoopGroup-3-1] io.netty.handler.logging.LoggingHandler  : [id: 0x57c00a34, L:/127.0.0.1:8080 - R:/127.0.0.1:8667] WRITE: 7B
         +-------------------------------------------------+
         |  0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f |
+--------+-------------------------------------------------+----------------+
|00000000| 68 65 6c 6c 6f 0d 0a                            |hello..         |
+--------+-------------------------------------------------+----------------+
2017-03-11 04:26:48.523  INFO 7356 --- [ntLoopGroup-3-1] io.netty.handler.logging.LoggingHandler  : [id: 0x57c00a34, L:/127.0.0.1:8080 - R:/127.0.0.1:8667] FLUSH