package org.rakam.kume.network; import io.netty.bootstrap.ServerBootstrap; import io.netty.buffer.PooledByteBufAllocator; import io.netty.channel.Channel; import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelInitializer; import io.netty.channel.ChannelOption; import io.netty.channel.ChannelPipeline; import io.netty.channel.EventLoopGroup; import io.netty.channel.socket.SocketChannel; import io.netty.channel.socket.nio.NioServerSocketChannel; import io.netty.handler.codec.LengthFieldBasedFrameDecoder; import io.netty.handler.codec.LengthFieldPrepender; import org.rakam.kume.service.Service; import org.rakam.kume.transport.PacketDecoder; import org.rakam.kume.transport.PacketEncoder; import org.rakam.kume.util.ThrowableNioEventLoopGroup; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.net.InetSocketAddress; import java.net.SocketAddress; import java.util.List; public class TCPServerHandler { final static Logger LOGGER = LoggerFactory.getLogger(TCPServerHandler.class); private final Channel server; public TCPServerHandler(EventLoopGroup bossGroup, EventLoopGroup workerGroup, ThrowableNioEventLoopGroup eventExecutor, List<Service> services, InetSocketAddress serverAddress) throws InterruptedException { ChannelFuture bind = new ServerBootstrap() .childOption(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT) .group(bossGroup, workerGroup) .channel(NioServerSocketChannel.class) .option(ChannelOption.AUTO_READ, false) .option(ChannelOption.SO_BACKLOG, 100) .childHandler(new ChannelInitializer<SocketChannel>() { @Override public void initChannel(SocketChannel ch) throws Exception { ChannelPipeline p = ch.pipeline(); p.addLast("frameDecoder", new LengthFieldBasedFrameDecoder(1048576, 0, 4, 0, 4)); p.addLast("packetDecoder", new PacketDecoder()); p.addLast("frameEncoder", new LengthFieldPrepender(Integer.BYTES)); p.addLast("packetEncoder", new PacketEncoder()); p.addLast(new ServerChannelAdapter(services, eventExecutor)); } }).bind(serverAddress); server = bind.sync() .addListener(future -> { if (!future.isSuccess()) { LOGGER.error("Failed to bind {}", bind.channel().localAddress()); } }).awaitUninterruptibly().channel(); } public ChannelFuture waitForClose() throws InterruptedException { return server.closeFuture().sync(); } public SocketAddress localAddress() { return server.localAddress(); } public void setAutoRead(boolean b) { server.config().setAutoRead(b); } public void close() throws InterruptedException { server.close().sync(); } }