Integración de conexiones Java, Netty, TCP y UDP: no hay espacio de búfer disponible para la conexión UDP

Tengo una aplicación que utiliza los protocolos TCP y UDP. La suposición principal es que el cliente se conecta al servidor a través del protocolo TCP y cuando se establece la conexión, se envían datagramas UDP. Tengo que admitir dos escenarios de conexión al servidor: - el cliente se conecta cuando el servidor se está ejecutando - el cliente se conecta cuando el servidor está inactivo y vuelve a intentar la conexión hasta que el servidor se inicia nuevamente

Para el primer escenario, todo funciona bastante bien: trabajé ambas conexiones. El problema es con el segundo escenario. Cuando el cliente intenta conectarse varias veces a través de TCP y finalmente se conecta, la función de conexión UDP genera una excepción:

java.net.SocketException: No buffer space available (maximum connections reached?): bind
at sun.nio.ch.Net.bind0(Native Method)
at sun.nio.ch.Net.bind(Net.java:344)
at sun.nio.ch.DatagramChannelImpl.bind(DatagramChannelImpl.java:684)
at sun.nio.ch.DatagramSocketAdaptor.bind(DatagramSocketAdaptor.java:91)
at io.netty.channel.socket.nio.NioDatagramChannel.doBind(NioDatagramChannel.java:192)
at io.netty.channel.AbstractChannel$AbstractUnsafe.bind(AbstractChannel.java:484)
at io.netty.channel.DefaultChannelPipeline$HeadContext.bind(DefaultChannelPipeline.java:1080)
at io.netty.channel.AbstractChannelHandlerContext.invokeBind(AbstractChannelHandlerContext.java:430)
at io.netty.channel.AbstractChannelHandlerContext.bind(AbstractChannelHandlerContext.java:415)
at io.netty.channel.DefaultChannelPipeline.bind(DefaultChannelPipeline.java:903)
at io.netty.channel.AbstractChannel.bind(AbstractChannel.java:197)
at io.netty.bootstrap.AbstractBootstrap$2.run(AbstractBootstrap.java:350)
at io.netty.util.concurrent.SingleThreadEventExecutor.runAllTasks(SingleThreadEventExecutor.java:380)
at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:357)
at io.netty.util.concurrent.SingleThreadEventExecutor$2.run(SingleThreadEventExecutor.java:116)
at io.netty.util.concurrent.DefaultThreadFactory$DefaultRunnableDecorator.run(DefaultThreadFactory.java:137)
at java.lang.Thread.run(Thread.java:722)

Cuando reinicio la aplicación del cliente sin hacer nada con el servidor, el cliente se conectará con cualquier problema.

¿Qué puede causar un problema?

A continuación adjunto el código fuente de las clases. Todo el código fuente proviene de ejemplos colocados en la página oficial del proyecto Netty. Lo único que he midificado es que reemplacé las variables y funciones estáticas por otras no estáticas. Se causó que en el futuro necesitaré muchas conexiones TCP-UDP a varios servidores.

public final class UptimeClient {
static final String HOST = System.getProperty("host", "192.168.2.193");
static final int PORT = Integer.parseInt(System.getProperty("port", "2011"));
static final int RECONNECT_DELAY = Integer.parseInt(System.getProperty("reconnectDelay", "5"));
static final int READ_TIMEOUT = Integer.parseInt(System.getProperty("readTimeout", "10"));

private static UptimeClientHandler handler;

public void runClient() throws Exception {
    configureBootstrap(new Bootstrap()).connect();
}

private Bootstrap configureBootstrap(Bootstrap b) {
    return configureBootstrap(b, new NioEventLoopGroup());
}

@Override
protected Object clone() throws CloneNotSupportedException {
    return super.clone(); //To change body of generated methods, choose Tools | Templates.
}

Bootstrap configureBootstrap(Bootstrap b, EventLoopGroup g) {
    if(handler == null){
            handler = new UptimeClientHandler(this);
    }
    b.group(g)
     .channel(NioSocketChannel.class)
     .remoteAddress(HOST, PORT)
     .handler(new ChannelInitializer<SocketChannel>() {
        @Override
        public void initChannel(SocketChannel ch) throws Exception {
            ch.pipeline().addLast(new IdleStateHandler(READ_TIMEOUT, 0, 0), handler);
        }
     });

    return b;
}

void connect(Bootstrap b) {
    b.connect().addListener(new ChannelFutureListener() {
        @Override
        public void operationComplete(ChannelFuture future) throws Exception {
            if (future.cause() != null) {
                handler.startTime = -1;
                handler.println("Failed to connect: " + future.cause());
            }
        }
    });
}
}


@Sharable
public class UptimeClientHandler extends SimpleChannelInboundHandler<Object> {
UptimeClient client;
public UptimeClientHandler(UptimeClient client){
    this.client = client;
}
long startTime = -1;

@Override
public void channelActive(ChannelHandlerContext ctx) {
    try {
        if (startTime < 0) {
            startTime = System.currentTimeMillis();
        }
        println("Connected to: " + ctx.channel().remoteAddress());
        new QuoteOfTheMomentClient(null).run();
    } catch (Exception ex) {
        Logger.getLogger(UptimeClientHandler.class.getName()).log(Level.SEVERE, null, ex);
    }
}

@Override
public void channelRead0(ChannelHandlerContext ctx, Object msg) throws Exception {
}

@Override
public void userEventTriggered(ChannelHandlerContext ctx, Object evt) {
    if (!(evt instanceof IdleStateEvent)) {
        return;
    }

    IdleStateEvent e = (IdleStateEvent) evt;
    if (e.state() == IdleState.READER_IDLE) {
        // The connection was OK but there was no traffic for last period.
        println("Disconnecting due to no inbound traffic");
        ctx.close();
    }
}

@Override
public void channelInactive(final ChannelHandlerContext ctx) {
    println("Disconnected from: " + ctx.channel().remoteAddress());
}

@Override
public void channelUnregistered(final ChannelHandlerContext ctx) throws Exception {
    println("Sleeping for: " + UptimeClient.RECONNECT_DELAY + 's');

    final EventLoop loop = ctx.channel().eventLoop();
    loop.schedule(new Runnable() {
        @Override
        public void run() {
            println("Reconnecting to: " + UptimeClient.HOST + ':' + UptimeClient.PORT);
            client.connect(client.configureBootstrap(new Bootstrap(), loop));
        }
    }, UptimeClient.RECONNECT_DELAY, TimeUnit.SECONDS);
}

@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
    cause.printStackTrace();
    ctx.close();
}

void println(String msg) {
    if (startTime < 0) {
        System.err.format("[SERVER IS DOWN] %s%n", msg);
    } else {
        System.err.format("[UPTIME: %5ds] %s%n", (System.currentTimeMillis() - startTime) / 1000, msg);
    }
    }
}

public final class QuoteOfTheMomentClient {

private ServerData config;
public QuoteOfTheMomentClient(ServerData config){
    this.config = config;
}

public void run() throws Exception {


    EventLoopGroup group = new NioEventLoopGroup();
    try {
        Bootstrap b = new Bootstrap();
        b.group(group)
         .channel(NioDatagramChannel.class)
         .option(ChannelOption.SO_BROADCAST, true)
         .handler(new QuoteOfTheMomentClientHandler());

        Channel ch = b.bind(0).sync().channel();

        ch.writeAndFlush(new DatagramPacket(
                Unpooled.copiedBuffer("QOTM?", CharsetUtil.UTF_8),
                new InetSocketAddress("192.168.2.193", 8193))).sync();

        if (!ch.closeFuture().await(5000)) {
            System.err.println("QOTM request timed out.");
        }
    }
    catch(Exception ex)
    {
        ex.printStackTrace();
    }
    finally {
        group.shutdownGracefully();
    }
    }
}

public class QuoteOfTheMomentClientHandler extends SimpleChannelInboundHandler<DatagramPacket> {

@Override
public void channelRead0(ChannelHandlerContext ctx, DatagramPacket msg) throws Exception {
    String response = msg.content().toString(CharsetUtil.UTF_8);
    if (response.startsWith("QOTM: ")) {
        System.out.println("Quote of the Moment: " + response.substring(6));
        ctx.close();
    }
}

@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
    cause.printStackTrace();
    ctx.close();
    }
}

Respuestas a la pregunta(1)

Su respuesta a la pregunta