具有AsynchronousServerSocketChannel的多线程服务器

时间:2015-10-09 17:31:04

标签: java multithreading java-7 nio

我必须实现一个应该接受更多连接的服务器。没有任何更深层次的想法,我决定使用新的JAVA NIO.2类。

我目前的做法是:

final Semaphore wait = new Semaphore(1);
while(true){
        wait.acquire();
        this.asyncSocket.accept(null, new CompletionHandler<AsynchronousSocketChannel, Void>() {
            @Override
            public void completed(AsynchronousSocketChannel result, Void attachment) {
                wait.release();
                asyncSocket.accept(null, this);
                ...
             }
             ...
        }
}

如果我不添加信号量,我会得到一个AcceptPendingException。但是,我不知道这是否是实现可以处理更多开放套接字的服务器的正确方法。

另一种方法是:

final Semaphore wait = new Semaphore(1);
    while(true){
        wait.acquire();
        final Future<AsynchronousSocketChannel> futureChannel = this.asyncSocket.accept();

        this.exec.execute(new Runnable() {
            @Override
            public void run() {
                try (final AsynchronousSocketChannel clientChannel = futureChannel.get()) {
                    wait.release();
                    try (ObjectInputStream ois = new ObjectInputStream(Channels.newInputStream(clientChannel))) {
                        final Command cmd = (Command) ois.readObject();

                        cmd.execute(util, clientChannel, null, logger).run();
                    }
                } catch (IOException | InterruptedException | ClassNotFoundException | ExecutionException e) {
                    e.printStackTrace();
                }
            }
        });

为什么我对这两种解决方案都不满意? 不幸的是,在这两种实现中,服务器都在TIME_WAIT状态下留下了很多打开的套接字,尽管我在客户端也在服务器上关闭它..

所以实际上我有两个问题:

  • 使用AsynchronousServerSocketChannel来实现一个接受更多连接的服务器的正确方法。
  • 如何摆脱TIME_WAIT状态下的打开套接字

编辑:

private <T extends Serializable> T sendCommand(final Command<T> command) throws ExecutionException, InterruptedException, IOException, ClassNotFoundException {
    T result = null;

    try (final AsynchronousSocketChannel channel = AsynchronousSocketChannel.open(channelGroup)) {
        channel.setOption(StandardSocketOptions.SO_REUSEADDR, true);
        channel.connect(this.mwInfo.getNextMiddleware()).get();

        final OutputStream os = Channels.newOutputStream(channel);
        final InputStream is = Channels.newInputStream(channel);

        try (final ObjectOutputStream oos = new ObjectOutputStream(os)) {
            oos.writeObject(command);
            oos.flush();

            try (final ObjectInputStream ois = new ObjectInputStream(is)) {
                result = (T) ois.readObject();
            }
        }
    }

    return result;
}

提前致谢!

1 个答案:

答案 0 :(得分:0)

我只能回答第二个问题(不了解Java套接字细节)。要摆脱这些套接字,必须在套接字上实现“graceful shutdown”协议。也就是说,一个套接字在发送时关闭,另一个套接字在发送时关闭,而不是在recv上关闭套接字。这将确保没有套接字保持TIME_WAIT状态。

另一种选择是摆弄套接字的SO_LINGER选项,但这是不明智的。

我还注意到,人们似乎只是使用SO_REUSEADDR作为通用解决方案。你不能绑定到端口?指定SO_REUSEADDR,你的所有问题都将消失......这是错误的!如果SO_REUSEADDR是通用解决方案,为什么默认情况下它不是ON?因为它很危险。当您指定SO_REUSEADDR时,您在同一端口上创建另一个套接字,现在您可以开始查看来自先前连接的消息了!当然,这不太可能发生。但它可能发生!想象一下,排除故障会出现什么样的错误!