Simple TCP Server
import java.util.Optional;
import java.util.concurrent.TimeUnit;
import java.util.function.Consumer;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.buffer.PooledByteBufAllocator;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.epoll.EpollChannelOption;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
public class SimpleTcpServer {
protected static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();
protected volatile int throughput = ~(Byte.MIN_VALUE << 1);
protected volatile int port = Short.MAX_VALUE;
protected volatile boolean closed = false;
protected volatile EventLoopGroup bossGroup;
protected volatile EventLoopGroup workerGroup;
protected volatile ServerBootstrap bootstrap;
public SimpleTcpServer(int port) {
this.port = port;
}
public SimpleTcpServer(int port, int throughput) {
this.port = port;
this.throughput = throughput;
}
public void stop() {
this.closed = true;
Optional.ofNullable(this.bossGroup).ifPresent(x -> x.shutdownGracefully());
Optional.ofNullable(this.workerGroup).ifPresent(x -> x.shutdownGracefully());
}
public void start(Consumer<SocketChannel> consumer) throws Exception {
this.closed = false;
this.bossGroup = new NioEventLoopGroup(CPU_COUNT + 1);
this.workerGroup = new NioEventLoopGroup();
this.bootstrap = new ServerBootstrap();
this.bootstrap.group(this.bossGroup, this.workerGroup)
.channel(NioServerSocketChannel.class)
.option(ChannelOption.SO_BACKLOG, this.throughput)
.option(ChannelOption.SO_REUSEADDR, true)
.option(EpollChannelOption.SO_REUSEPORT, true)
.childOption(ChannelOption.SO_KEEPALIVE, true)
.childOption(ChannelOption.TCP_NODELAY, true)
.childOption(ChannelOption.SO_LINGER, 0)
.option(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT)
.childOption(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT)
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
public void initChannel(SocketChannel ch) throws Exception {
Optional.ofNullable(consumer).ifPresent(x -> x.accept(ch));
};
});
doBind();
}
protected void doBind() {
if (this.closed) {
return;
}
this.bootstrap.bind(this.port).addListener(new ChannelFutureListener() {
@Override
public void operationComplete(ChannelFuture cf) throws Exception {
if (!cf.isSuccess()) {
cf.channel().eventLoop().schedule(() -> doBind(), 1, TimeUnit.SECONDS);
}
}
});
}
}
Simple TCP Client
import java.util.Optional;
import java.util.concurrent.TimeUnit;
import java.util.function.Consumer;
import io.netty.bootstrap.Bootstrap;
import io.netty.buffer.PooledByteBufAllocator;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
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.NioSocketChannel;
import io.netty.handler.timeout.IdleState;
import io.netty.handler.timeout.IdleStateEvent;
import io.netty.handler.timeout.IdleStateHandler;
public class SimpleTcpClient {
protected volatile String addr = "127.0.0.1";
protected volatile int port = Short.MAX_VALUE;
protected volatile boolean closed = false;
protected volatile EventLoopGroup workerGroup;
protected volatile Bootstrap bootstrap;
protected volatile Channel channel;
public SimpleTcpClient(String addr, int port) {
this.addr = addr;
this.port = port;
}
public void stop() {
this.closed = true;
Optional.ofNullable(this.channel).ifPresent(x -> x.close());
Optional.ofNullable(this.workerGroup).ifPresent(x -> x.shutdownGracefully());
}
public void start(Consumer<SocketChannel> consumer) throws Exception {
this.closed = false;
this.workerGroup = new NioEventLoopGroup();
this.bootstrap = new Bootstrap();
this.bootstrap.group(this.workerGroup)
.channel(NioSocketChannel.class)
.option(ChannelOption.TCP_NODELAY, true)
.option(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT)
.handler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel ch) throws Exception {
ch.pipeline().addFirst(new ChannelInboundHandlerAdapter() {
@Override
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
super.channelInactive(ctx);
ctx.channel().eventLoop().schedule(() -> doConnect(), 1, TimeUnit.SECONDS);
}
@Override
public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
super.userEventTriggered(ctx, evt);
if (evt instanceof IdleStateEvent) {
IdleStateEvent event = (IdleStateEvent) evt;
if (event.state() == IdleState.READER_IDLE) {
ctx.channel().close();
}
}
}
});
ch.pipeline().addLast(new IdleStateHandler(5, 0, 0, TimeUnit.SECONDS));
Optional.ofNullable(consumer).ifPresent(x -> x.accept(ch));
}
});
doConnect();
}
protected void doConnect() {
if (this.closed) {
return;
}
this.bootstrap.connect(this.addr, this.port)
.addListener(new ChannelFutureListener() {
public void operationComplete(ChannelFuture cf) throws Exception {
if (!cf.isSuccess()) {
cf.channel().eventLoop().schedule(() -> doConnect(), 1, TimeUnit.SECONDS);
} else {
channel = cf.channel();
}
}
});
}
public void sent(Object data) {
Optional.ofNullable(this.channel)
.filter(x -> x.isActive() && x.isWritable())
.ifPresent(x -> x.writeAndFlush(data));
}
}