2015-03-31 19 views
0

我有使用TCP和UDP协议的应用程序。主要假设是客户端通过TCP协议连接到服务器,当连接建立时,UDP数据报正在发送。 我要支持连接到服务器的两种情况: - 当服务器运行 客户端连接 - 当服务器已关闭,直到服务器再次Java,Netty,TCP和UDP连接集成:没有可用于UDP连接的缓冲区空间

首次启动的情况一切正常精致漂亮重试连接客户端连接:我工作两个连接。 问题出在第二种情况。当客户端尝试几次通过TCP连接,最终连接,UDP连接功能抛出异常:

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) 

当我重新启动客户端应用程序没有做与任何服务器,客户端将与任何问题连接。

什么会导致问题?

在下面我附上类的源代码。所有源代码都来自官方Netty项目页面中的示例。我中断的唯一的事情是我用非静态变量替换了静态变量和函数。这是由于将来我需要很多TCP-UDP连接到多个服务器。

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(); 
    } 
} 
+0

您不需要创建多个UDP套接字。只需使用相同的一切。 – EJP 2015-04-01 00:05:10

回答

2

如果你的服务器为Windows Server 2008(R2或R2 SP1),这个问题可能是由this stackoverflow answer是指Microsoft KB article #2577795

发生的,因为在一个竞争条件问题描述和解决针对导致套接字泄漏的WinSock(Afd.sys)的辅助功能驱动程序 。随着时间的推移,如果所有可用套接字 资源都耗尽,则会出现“症状”一节中描述的问题 。


如果你的服务器是Windows Server 2003中,这个问题可能是由this stackoverflow answer是指Microsoft KB article #196271

的临时TCP端口的默认最大数量的描述和解决的是5000的的产品, 包含在“适用于”部分。这些产品中新增了一个参数 。为了增加临时端口的最大数量,请按照下列步骤 ...

...这基本上意味着你已经用完了临时端口。