[Kotlin, Netty] Echo Server 구현하기
카테고리: Java + Kotlin
Netty
Netty는 비동기 이벤트 기반 네트워크 애플리케이션 프레임워크이다. Java 기반의 NIO(Non-blocking I/O)를 바탕으로 서버 및 클라이언트와 같은 네트워크 애플리케이션을 빠르고 쉽게 개발할 수 있다.
이전에 자주 사용했던 MINA와 Netty는 NIO를 사용한다는 점에선 같지만, 많은 부분에서 다르다. Netty는 성능과 확장성을 우선시한 모듈식 설계와 더 큰 커뮤니티를 바탕으로 개발자가 네트워크 운영을 위한 프로그램을 작성할 수 있다. 또한, Netty의 아키텍처와 스레딩 모델은 처리량이 많은 서버 애플리케이션을 위한 탁월한 성능을 제공한다.
https://stackshare.io/stackups/mina-vs-netty
EchoServer 예제
- MainServer.kt
import io.netty.bootstrap.ServerBootstrap
import io.netty.channel.ChannelInitializer
import io.netty.channel.ChannelOption
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
object MainServer {
@JvmStatic
fun main(args: Array<String>) {
// Configure the server.
val bossGroup: EventLoopGroup = NioEventLoopGroup(1)
val workerGroup: EventLoopGroup = NioEventLoopGroup()
val serverHandler = MainServerHandler()
try {
val b = ServerBootstrap()
b.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel::class.java)
.handler(LoggingHandler(LogLevel.INFO))
.option(ChannelOption.SO_BACKLOG, 100)
.childHandler(object : ChannelInitializer<SocketChannel>() {
public override fun initChannel(ch: SocketChannel) {
val p = ch.pipeline()
p.addLast(Codec.PacketDecoder())
p.addLast(Codec.PacketEncoder())
p.addLast(serverHandler)
}
})
// Start the server.
val f = b.bind(Setting().serverPort).sync()
// Wait until the server socket is closed.
f.channel().closeFuture().sync()
} finally {
// Shut down all event loops to terminate all threads.
bossGroup.shutdownGracefully()
workerGroup.shutdownGracefully()
}
}
}
- MainServerHandler.kt
import io.netty.channel.Channel
import io.netty.channel.ChannelHandler.Sharable
import io.netty.channel.ChannelHandlerContext
import io.netty.channel.ChannelInboundHandlerAdapter
@Sharable
class MainServerHandler : ChannelInboundHandlerAdapter() {
override fun channelActive(ctx: ChannelHandlerContext) {
println("[Connect] " + ctx.channel().remoteAddress())
}
override fun channelInactive(ctx: ChannelHandlerContext) {
println("[DisConnect] " + ctx.channel().remoteAddress())
ctx.channel().close()
}
override fun channelRead(ctx: ChannelHandlerContext, msg: Any?) {
println("[Send] " + channel.remoteAddress() + " " + msg)
ctx.write(msg)
}
override fun channelReadComplete(ctx: ChannelHandlerContext) {
ctx.flush()
}
override fun exceptionCaught(ctx: ChannelHandlerContext, cause: Throwable) {
// Close the connection when an exception is raised.
cause.printStackTrace()
ctx.close()
}
}
재작성 하면서 느낀점
- Java와 호환을 위한 어노테이션이 많다.
- Java 10부터도 var이 생겨서 추론형 변수를 알고 있었으나, val이라는 새로운 것이 등장해서 뭔가 싶었지만, final 같은 불변 타입이었다.
- override를 할 때 어노테이션이 아니라, 함수 선언 앞에 붙는다.
- 함수를 선언할 때 fun이라고 선언한다.
- 매개변수의 형식은 콜론 뒤에 붙으며, ?가 붙을 때만 nullable로 null을 허용해준다.
- new를 사용한 객체 생성이 없다.