Structure
Netty: ๊ตฌ์ฑ ์์ ๋ฐ ์ค๊ณ
์ด๋ฒ ํฌ์คํธ์์๋ Netty์ ์ฃผ์ ๊ตฌ์ฑ ์์์ ์ค๊ณ ์๋ฆฌ์ ๋ํด ์์ธํ ์์๋ณด๊ฒ ์ต๋๋ค.
3.1 ์ฑ๋, ์ด๋ฒคํธ ๋ฃจํ ๋ฐ ์ฑ๋ ํจ์ฒ
Netty์ ์ฃผ์ ๊ตฌ์ฑ ์์๋ Channel, EventLoop, ๊ทธ๋ฆฌ๊ณ ChannelFuture์
๋๋ค. ์ด๋ค ๊ฐ๊ฐ์ด ๋ฌด์์ธ์ง, ๊ทธ๋ฆฌ๊ณ ์ด๋ป๊ฒ ๋์ํ๋์ง ์ดํด๋ณด๊ฒ ์ต๋๋ค.
3.1.1 ์ฑ๋(Channel)
์ฑ๋์ ๋คํธ์ํฌ ์ฐ๊ฒฐ์ ์ถ์ํํ ๊ฐ์ฒด์
๋๋ค. Netty์์๋ ๋ค์ํ ์ ํ์ ์ฑ๋์ ์ ๊ณตํฉ๋๋ค. NioSocketChannel์ ๋น์ฐจ๋จํ I/O ์์ผ ์ฑ๋์
๋๋ค. ์ฑ๋์ ๋ฐ์ดํฐ๋ฅผ ์ก์์ ํ๊ธฐ ์ํ ์ฃผ์ ์ธํฐํ์ด์ค์
๋๋ค.
import io.netty.channel.Channel
// ์ฑ๋ ์ธํฐํ์ด์ค
interface MyChannel : Channel {
// ๋ฐ์ดํฐ ์ก์์ ๋ฉ์๋ ์ ์
}3.1.2 ์ด๋ฒคํธ ๋ฃจํ(EventLoop)
์ด๋ฒคํธ ๋ฃจํ๋ ์ฑ๋์ ๋ชจ๋ ์ด๋ฒคํธ๋ฅผ ์ฒ๋ฆฌํ๋ ๋จ์ผ ์ค๋ ๋์ ๋๋ค. Netty๋ ์ฑ๋๋น ํ๋์ ์ด๋ฒคํธ ๋ฃจํ๋ฅผ ํ ๋นํ์ฌ, ํด๋น ์ฑ๋์ ๋ํ ๋ชจ๋ I/O ์์ ์ ์ฒ๋ฆฌํฉ๋๋ค.
import io.netty.channel.EventLoop
// ์ด๋ฒคํธ ๋ฃจํ ์ธํฐํ์ด์ค
interface MyEventLoop : EventLoop {
// ์ด๋ฒคํธ ์ฒ๋ฆฌ ๋ฉ์๋ ์ ์
}3.1.3 ์ฑ๋ ํจ์ฒ(ChannelFuture)
์ฑ๋ ํจ์ฒ๋ ๋น๋๊ธฐ ์์
์ ๊ฒฐ๊ณผ๋ฅผ ๋ํ๋ด๋ ๊ฐ์ฒด์
๋๋ค. ์ด๋ ์์
์ด ์๋ฃ๋์์ ๋ ์๋ฆผ์ ๋ฐ๊ณ , ๊ฒฐ๊ณผ๋ฅผ ํ์ธํ ์ ์๋ ๋ฐฉ๋ฒ์ ์ ๊ณตํฉ๋๋ค. Netty์ ChannelFuture ์ธํฐํ์ด์ค๋ ๋น๋๊ธฐ ์์
์ ์ํ๋ฅผ ๋ํ๋ด๋ฉฐ, ์์
์ด ์๋ฃ๋๋ฉด ๋ฆฌ์ค๋๋ฅผ ํตํด ์๋ฆผ์ ๋ฐ์ต๋๋ค.
import io.netty.channel.ChannelFuture
// ์ฑ๋ ํจ์ฒ ์ธํฐํ์ด์ค
interface MyChannelFuture : ChannelFuture {
// ๋น๋๊ธฐ ์์
์ํ ํ์ธ ๋ฉ์๋ ์ ์
}3.1.4 ๊ตฌ์ฑ ์์ ๋ค์ด์ด๊ทธ๋จ
3.2 ์ฑ๋ ํธ๋ค๋ฌ ๋ฐ ์ฑ๋ ํ์ดํ๋ผ์ธ
Netty์ ์ฃผ์ ๊ฐ๋
์ค ํ๋๋ ChannelHandler์ ChannelPipeline์
๋๋ค.
3.2.1 ์ฑ๋ ํธ๋ค๋ฌ(ChannelHandler)
์ฑ๋ ํธ๋ค๋ฌ๋ ๋คํธ์ํฌ ์ด๋ฒคํธ๋ฅผ ์ฒ๋ฆฌํ๋ ๋ก์ง์ ๊ตฌํํฉ๋๋ค. ์๋ฅผ ๋ค์ด, ์์ ๋ ๋ฐ์ดํฐ๋ฅผ ์ฒ๋ฆฌํ๊ฑฐ๋, ์ก์ ํ ๋ฐ์ดํฐ๋ฅผ ์ค๋นํฉ๋๋ค. ChannelInboundHandler๋ ์์ ๋ ๋ฐ์ดํฐ๋ฅผ ์ฒ๋ฆฌํ๊ณ , ChannelOutboundHandler๋ ์ก์ ํ ๋ฐ์ดํฐ๋ฅผ ์ฒ๋ฆฌํฉ๋๋ค.
import io.netty.channel.ChannelHandlerContext
import io.netty.channel.ChannelInboundHandlerAdapter
class MyChannelHandler : ChannelInboundHandlerAdapter() {
override fun channelRead(ctx: ChannelHandlerContext, msg: Any) {
// ์์ ๋ ๋ฉ์์ง ์ฒ๋ฆฌ
println("Received message: $msg")
ctx.writeAndFlush(msg)
}
override fun exceptionCaught(ctx: ChannelHandlerContext, cause: Throwable) {
cause.printStackTrace()
ctx.close()
}
}3.2.2 ์ฑ๋ ํ์ดํ๋ผ์ธ(ChannelPipeline)
์ฑ๋ ํ์ดํ๋ผ์ธ์ ์ฌ๋ฌ ๊ฐ์ ์ฑ๋ ํธ๋ค๋ฌ๋ฅผ ์ฒด์ธ์ผ๋ก ์ฐ๊ฒฐํ ๊ฒ์ ๋๋ค. ๋ฐ์ดํฐ๊ฐ ์ฑ๋์ ํตํด ์ ์ก๋ ๋, ๊ฐ ํธ๋ค๋ฌ๋ ์์ฐจ์ ์ผ๋ก ๋ฐ์ดํฐ๋ฅผ ์ฒ๋ฆฌํฉ๋๋ค. ํ์ดํ๋ผ์ธ์ ๋ฐ์ดํฐ๋ฅผ ๋ณํํ๊ฑฐ๋, ๋ก๊น ํ๊ฑฐ๋, ์ธ์ฆ์ ์ํํ๋ ๋ฐ ์ฌ์ฉ๋ฉ๋๋ค.
import io.netty.channel.ChannelInitializer
import io.netty.channel.socket.SocketChannel
class MyChannelInitializer : ChannelInitializer<SocketChannel>() {
override fun initChannel(ch: SocketChannel) {
ch.pipeline().addLast(MyChannelHandler())
}
}3.3 ๋ถํธ์คํธ๋ํ(Bootstrapping)
๋ถํธ์คํธ๋ํ์ Netty ์ ํ๋ฆฌ์ผ์ด์ ์ ์ค์ ํ๊ณ ์์ํ๋ ๊ณผ์ ์ ๋๋ค. ์๋ฒ์ ํด๋ผ์ด์ธํธ ๋ชจ๋ ๋ถํธ์คํธ๋ฉ ํด๋์ค๊ฐ ์์ผ๋ฉฐ, ์ด๋ค์ ๋คํธ์ํฌ ์ค์ ์ ๊ด๋ฆฌํฉ๋๋ค.
3.3.1 ์๋ฒ ๋ถํธ์คํธ๋ฉ(ServerBootstrap)
์๋ฒ ๋ถํธ์คํธ๋ฉ์ ์๋ฒ ์ ํ๋ฆฌ์ผ์ด์ ์ ์ค์ ํ๊ณ ์์ํฉ๋๋ค. ์ด๋ฅผ ํตํด ์๋ฒ ์์ผ์ ๋ฐ์ธ๋ฉํ๊ณ , ํด๋ผ์ด์ธํธ ์ฐ๊ฒฐ์ ๊ธฐ๋ค๋ฆฝ๋๋ค.
import io.netty.bootstrap.ServerBootstrap
import io.netty.channel.nio.NioEventLoopGroup
import io.netty.channel.socket.nio.NioServerSocketChannel
fun startServer(port: Int) {
val bossGroup = NioEventLoopGroup()
val workerGroup = NioEventLoopGroup()
try {
val b = ServerBootstrap()
b.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel::class.java)
.childHandler(MyChannelInitializer())
.option(ChannelOption.SO_BACKLOG, 128)
.childOption(ChannelOption.SO_KEEPALIVE, true)
val f = b.bind(port).sync()
f.channel().closeFuture().sync()
} finally {
workerGroup.shutdownGracefully()
bossGroup.shutdownGracefully()
}
}3.3.2 ํด๋ผ์ด์ธํธ ๋ถํธ์คํธ๋ฉ(Bootstrap)
ํด๋ผ์ด์ธํธ ๋ถํธ์คํธ๋ฉ์ ํด๋ผ์ด์ธํธ ์ ํ๋ฆฌ์ผ์ด์ ์ ์ค์ ํ๊ณ ์์ํฉ๋๋ค. ์ด๋ฅผ ํตํด ์๋ฒ์ ์ฐ๊ฒฐํ๊ณ , ๋ฐ์ดํฐ๋ฅผ ์ก์์ ํฉ๋๋ค.
import io.netty.bootstrap.Bootstrap
import io.netty.channel.nio.NioEventLoopGroup
import io.netty.channel.socket.nio.NioSocketChannel
fun startClient(host: String, port: Int) {
val group = NioEventLoopGroup()
try {
val b = Bootstrap()
b.group(group)
.channel(NioSocketChannel::class.java)
.handler(MyChannelInitializer())
val f = b.connect(host, port).sync()
f.channel().closeFuture().sync()
} finally {
group.shutdownGracefully()
}
}Last updated