📚 [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