【问题标题】:How should I use AsynchronousServerSocketChannel for accepting connections?我应该如何使用 AsynchronousServerSocketChannel 来接受连接?
【发布时间】:2012-01-20 11:31:22
【问题描述】:

我想使用 Java 7 和 NIO 2 编写一个异步服务器。

但是我应该如何使用AsynchronousServerSocketChannel

例如如果我开始:

final AsynchronousServerSocketChannel server = 
    AsynchronousServerSocketChannel.open().bind(
        new InetSocketAddress(port));

然后当我执行server.accept() 时,程序终止,因为该调用是异步。如果我将该代码放入无限循环中,则会抛出 AcceptPendingException

关于如何使用AsynchronousServerSocketChannel 编写简单的异步服务器有什么建议吗?

这是我的完整示例(类似于 JavaDoc 中的示例):

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.channels.AsynchronousServerSocketChannel;
import java.nio.channels.AsynchronousSocketChannel;
import java.nio.channels.CompletionHandler;

public class AsyncServer {

    public static void main(String[] args) {
        int port = 8060;
        try {
            final AsynchronousServerSocketChannel server = 
                    AsynchronousServerSocketChannel.open().bind(
                            new InetSocketAddress(port));

            System.out.println("Server listening on " + port);

            server.accept("Client connection", 
                    new CompletionHandler<AsynchronousSocketChannel, Object>() {
                public void completed(AsynchronousSocketChannel ch, Object att) {
                    System.out.println("Accepted a connection");

                    // accept the next connection
                    server.accept("Client connection", this);

                    // handle this connection
                    //TODO handle(ch);
                }

                public void failed(Throwable exc, Object att) {
                    System.out.println("Failed to accept connection");
                }
            });
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

【问题讨论】:

  • 您可以使用专门用于客户端-服务器应用程序的 Netty 框架。它还使用 java NIO。服务器开发简单快捷。通过netty.io
  • @Optimus:我知道netty,但这与这个问题无关。

标签: java asynchronous nio java-7


【解决方案1】:

您在正确的轨道上,从完成的回调中调用 accept() 以接受更多连接应该可以工作。

防止线程终止的一种简单(但丑陋)的方法是简单地循环直到线程被中断。

// yes, sleep() is evil, but sometimes I don't care
while (true) {
    Thread.sleep(1000);
}

更简洁的方法是使用AsynchronousChannelGroup。例如:

AsynchronousChannelGroup group = AsynchronousChannelGroup.withThreadPool(Executors
            .newSingleThreadExecutor());
AsynchronousServerSocketChannel server = AsynchronousServerSocketChannel.open(group).bind(
            new InetSocketAddress(port));

// (insert server.accept() logic here)

// wait until group.shutdown()/shutdownNow(), or the thread is interrupted:
group.awaitTermination(Long.MAX_VALUE, TimeUnit.SECONDS);

您可以调整线程的处理方式,有关详细信息,请参阅AsynchronousChannelGroup API docs

【讨论】:

  • // yes, sleep() is evil, but sometimes I don't care 你应该关心。
  • 嘿。尽管我称它为 uglyevil,但可能是因为 sleep() 调用,所以对此一直投反对票,然后演示了一个更好的方法。这似乎很教条。 :-)
【解决方案2】:

如果您在同一个线程中有其他事情要做,则使用异步接受很有用。在您的情况下,您没有做其他事情,所以我会使用

while(true) {
    AsynchronousSocketChannel socket = server.accept().get();
    System.out.println("Accepted " + socket);
    socket.close();
}

【讨论】:

  • @gnarly 确实如此,但 accept() 每次返回不同的 Future。
  • 我的意思是你的例子是一个阻塞服务器,这违背了AsynchronousServerSocketChannel的目的。
  • @gnarly 只有接受线程阻塞。如果你有一个处理程序线程来处理连接的套接字,这不是问题。
  • 你不应该在异步服务器中使用多个线程,这就是它的美妙之处,操作系统会在内部处理它,并在需要完成 IO 时通知你,因此你永远不应该拥有线程等待它。使用CompletionHandler,它会自动侦听要接受的连接。
  • @gnarly 当然,Java 使用 ExecutorService 来执行读/写并返回 Futures 除外。它不使用操作系统来执行异步操作(可能除了 inifini-band)。 ;)
【解决方案3】:

另一种选择是让您的 main 方法在返回之前等待信号。然后,如果你有某种外部关闭命令,你只需通知信号,主线程就会关闭。

private static final Object shutdownSignal = new Object();

public static void main(String[] args) {

    ...

    synchronized (shutdownSignal) {
        try {
            shutdownSignal.wait();
        }
        catch (InterruptedException e) {
            // handle it!
        }
    }
}

【讨论】:

  • 我不会使用“Object”作为标识符,既不是布尔值,也不是布尔值类,因为它会中断,给你留下奇怪的副作用(阅读源代码为什么)最佳解决方案:〜在私人决赛上同步专门为此目的指定的对象(如果其他人可能扩展我们的类,则更整洁)~ 将 shutdownSignal 替换为您可以使用 get 和 set 方法而不是赋值来更改的最终 AtomicBoolean(您仍然需要同步以测试状态)来源: telliott.io/node/40(为什么不在同步时使用布尔值)
  • @JasperLankhorst 您的评论毫无意义。这正是等待/通知的用例。如您的源文章中所述,没有可能出现奇怪的副作用,因为这里没有发生分配。这只是一种信号机制。
【解决方案4】:

使用倒计时锁存器,如下例所示

    final AsynchronousServerSocketChannel serverChannel = AsynchronousServerSocketChannel.open();
    InetSocketAddress address = new InetSocketAddress(port);
    serverChannel.bind(address);
    final CountDownLatch latch = new CountDownLatch(1);
    serverChannel.accept(null, new CompletionHandler<AsynchronousSocketChannel, Object>() {
@Override
        public void completed(final AsynchronousSocketChannel channel, Object attachment) {
            serverChannel.accept(null, this);
                        }

});
try {
        latch.await();
    } catch (InterruptedException e) {
        e.printStackTrace();
        Thread.currentThread().interrupt();
    }

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2017-12-13
    • 2013-02-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-05-27
    • 2011-08-21
    • 1970-01-01
    相关资源
    最近更新 更多