Echo Server

Echo ์„œ๋ฒ„๋Š” ํด๋ผ์ด์–ธํŠธ๋กœ๋ถ€ํ„ฐ ๋ฉ”์‹œ์ง€๋ฅผ ๋ฐ›์•„ ๊ทธ๋Œ€๋กœ ๋ฐ˜ํ™˜ํ•˜๋Š” ์„œ๋ฒ„์ž…๋‹ˆ๋‹ค.

implementation 'org.springframework.boot:spring-boot-starter-webflux'

2.1 ChannelHandler ๊ตฌํ˜„

๋จผ์ € ํด๋ผ์ด์–ธํŠธ์˜ ์š”์ฒญ์„ ์ฒ˜๋ฆฌํ•˜๋Š” ChannelHandler๋ฅผ ๊ตฌํ˜„ํ•ฉ๋‹ˆ๋‹ค. ์ด ํ•ธ๋“ค๋Ÿฌ๋Š” ์ˆ˜์‹ ๋œ ๋ฉ”์‹œ์ง€๋ฅผ ๊ทธ๋Œ€๋กœ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค.

@Component
class EchoServerHandler : ChannelInboundHandlerAdapter() {
    private val logger =  LoggerFactory.getILoggerFactory().getLogger(EchoServerHandler::class.java.name)

    override fun channelRead(ctx: ChannelHandlerContext, msg: Any) {
        logger.info("[Echo] Received: ${(msg as ByteBuf).toString(Charset.defaultCharset())}")
        ctx.write(msg)
    }

    override fun channelReadComplete(ctx: ChannelHandlerContext) {
        ctx.writeAndFlush(Unpooled.EMPTY_BUFFER)
            .addListener(CLOSE)
    }

    override fun channelActive(ctx: ChannelHandlerContext) {
        logger.info("[Echo] Connected to client")
    }
}

2.2 ์„œ๋ฒ„ ๋ถ€ํŠธ์ŠคํŠธ๋žฉ ์„ค์ •

์„œ๋ฒ„๋ฅผ ์„ค์ •ํ•˜๊ณ  ์‹œ์ž‘ํ•˜๊ธฐ ์œ„ํ•œ ๋ถ€ํŠธ์ŠคํŠธ๋žฉ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•ฉ๋‹ˆ๋‹ค.

@Configuration
class EchoServer (
    @Value("\${netty.port:28080}")
    private val port: Int,
    private val echoServerHandler: EchoServerHandler,
) {
    private val logger: Logger = LoggerFactory.getLogger(EchoServer::class.java)
    private val group: NioEventLoopGroup = NioEventLoopGroup()
    private val serverBootstrap: ServerBootstrap = ServerBootstrap()

    @Bean
    fun start(): ServerBootstrap {
        logger.info("[Echo] Starting server on port $port")
        return serverBootstrap.group(group)
            .channel(NioServerSocketChannel::class.java)
            .localAddress(port)
            .childHandler(echoServerHandler)
            .also { it.bind().sync() }
    }

    @PreDestroy
    fun stop() {
        logger.info("[Echo] Stopping server")
        group.shutdownGracefully().sync()
    }
}

3. Echo ํด๋ผ์ด์–ธํŠธ ์ž‘์„ฑ

Echo ์„œ๋ฒ„์™€ ํ†ต์‹ ํ•  ํด๋ผ์ด์–ธํŠธ๋ฅผ ์ž‘์„ฑํ•ด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

3.1 ChannelHandler ๊ตฌํ˜„

ํด๋ผ์ด์–ธํŠธ์˜ ChannelHandler๋Š” ์„œ๋ฒ„๋กœ๋ถ€ํ„ฐ ์ˆ˜์‹ ๋œ ๋ฐ์ดํ„ฐ๋ฅผ ์ฒ˜๋ฆฌํ•ฉ๋‹ˆ๋‹ค.

class EchoClientHandler : SimpleChannelInboundHandler<ByteBuf>() {
    private val logger = getILoggerFactory().getLogger(EchoClientHandler::class.java.name)
    override fun channelRead0(p0: ChannelHandlerContext?, p1: ByteBuf?) {
        logger.info("[Echo] Received: ${p1?.toString(Charset.defaultCharset())}")
    }
    override fun channelActive(ctx: ChannelHandlerContext) {
        logger.info("[Echo] Connected to server")
        ctx.writeAndFlush(Unpooled.copiedBuffer("Hello, World!".toByteArray()))
    }
}

3.2 ํด๋ผ์ด์–ธํŠธ ๋ถ€ํŠธ์ŠคํŠธ๋žฉ ์„ค์ •

ํด๋ผ์ด์–ธํŠธ ๋ถ€ํŠธ์ŠคํŠธ๋žฉ ์ฝ”๋“œ๋Š” ์„œ๋ฒ„์— ์—ฐ๊ฒฐํ•˜๊ณ  ๋ฉ”์‹œ์ง€๋ฅผ ์ „์†กํ•˜๋Š” ์ž‘์—…์„ ์ฒ˜๋ฆฌํ•ฉ๋‹ˆ๋‹ค.

@Test
fun echoPingTest() {
    val echoClientHandler = EchoClientHandler()
    val group = NioEventLoopGroup()
    val serverBootstrap = Bootstrap()
        .group(group)
        .channel(NioSocketChannel::class.java)
        .remoteAddress("localhost", 28080)
        .handler(object : ChannelInitializer<SocketChannel>() {
            override fun initChannel(ch: SocketChannel) {
                ch.pipeline().addLast(echoClientHandler)
            }
        })
    val channelFuture = serverBootstrap.connect().sync()
    channelFuture.channel().closeFuture().sync()
}

4. ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์‹คํ–‰

์ด์ œ ์„œ๋ฒ„์™€ ํด๋ผ์ด์–ธํŠธ๋ฅผ ์‹คํ–‰ํ•˜์—ฌ Netty ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ํ…Œ์ŠคํŠธํ•ด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

  1. ๋จผ์ € EchoServer์˜ main ๋ฉ”์„œ๋“œ๋ฅผ ์‹คํ–‰ํ•˜์—ฌ ์„œ๋ฒ„๋ฅผ ์‹œ์ž‘ํ•ฉ๋‹ˆ๋‹ค.

  2. ๊ทธ๋Ÿฐ ๋‹ค์Œ EchoClient์˜ main ๋ฉ”์„œ๋“œ๋ฅผ ์‹คํ–‰ํ•˜์—ฌ ํด๋ผ์ด์–ธํŠธ๋ฅผ ์‹œ์ž‘ํ•ฉ๋‹ˆ๋‹ค.

ํด๋ผ์ด์–ธํŠธ๋Š” ์„œ๋ฒ„์— ๋ฉ”์‹œ์ง€๋ฅผ ์ „์†กํ•˜๊ณ , ์„œ๋ฒ„๋Š” ๊ทธ ๋ฉ”์‹œ์ง€๋ฅผ ๊ทธ๋Œ€๋กœ ํด๋ผ์ด์–ธํŠธ์— ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค.

Last updated

Was this helpful?