如何避免使用Java ServerSocket阻塞?

我正在研究一个套接字监听器,它必须在2个端口上侦听2种类型的数据(端口80和端口81)。 这些数据与对数据执行的操作类型非常相似,只是因为它们到达不同的端口而不同。 我继续使用Java的ServerSocket类编写了一个实现,但后来才意识到ServerSocket类的accept()方法是块,我的实现无法承受。 所以现在我正在考虑使用Java NIO实现相同的function,但在完成了一些教程之后,我认为我比我的开始更困惑。 如果这里的某个人可以引导我完成整个过程,即使它是伪代码或技术“下一步该做什么”,那将是很棒的。 这就是我打算实现的目标。

通过调用2个类似的线程来监听,就像永远在2个端口上一样。(非阻塞)来自某个网​​络位置的远程设备连接,发送数据然后断开连接。

我想如果只知道如何使用NIO来设置服务器来监听端口,那么就可以实现localhost上的端口80,其余部分都很容易实现。

干杯

当您需要扩展到数千个并发连接时,需要NIO。

否则,我建议使用多个线程。 对于每个端口(及其相应的ServerSocket ),创建一个在循环中调用accept()的线程。 这些调用会阻塞,但这没关系,因为其他线程正在运行,负责任何可用的任务。

接受新Socket ,创建专用于该连接的另一个线程。 它取决于应用程序,但通常此线程将从套接字读取(阻塞操作),并执行请求的操作,将结果写回套接字。

在大多数桌面平台上,此体系结构将扩展到数百个连接。 编程模型非常简单,只要每个连接都是独立的并且独立于其他连接(这可以避免并发问题)。 引入NIO将提供更多可扩展性,但需要很多复杂性。

这里有一个开始使用NIO的小例子。

它是一个侦听端口80和81的服务器,并打印标准输出上接收的所有内容。 收到以CLOSE开头的数据包后CLOSE ; 收到以QUIT开头的数据包后,整个服务器都会关闭。 缺少发送部分和error handling可能会好一点。 🙂

 public static void main() throws IOException { ByteBuffer buffer = ByteBuffer.allocate(1024); Selector selector = Selector.open(); ServerSocketChannel server1 = ServerSocketChannel.open(); server1.configureBlocking(false); server1.socket().bind(new InetSocketAddress(80)); server1.register(selector, OP_ACCEPT); ServerSocketChannel server2 = ServerSocketChannel.open(); server2.configureBlocking(false); server2.socket().bind(new InetSocketAddress(81)); server2.register(selector, OP_ACCEPT); while (true) { selector.select(); Iterator iter = selector.selectedKeys().iterator(); while (iter.hasNext()) { SocketChannel client; SelectionKey key = iter.next(); iter.remove(); switch (key.readyOps()) { case OP_ACCEPT: client = ((ServerSocketChannel) key.channel()).accept(); client.configureBlocking(false); client.register(selector, OP_READ); break; case OP_READ: client = (SocketChannel) key.channel(); buffer.clear(); if (client.read(buffer) != -1) { buffer.flip(); String line = new String(buffer.array(), buffer.position(), buffer.remaining()); System.out.println(line); if (line.startsWith("CLOSE")) { client.close(); } else if (line.startsWith("QUIT")) { for (SelectionKey k : selector.keys()) { k.cancel(); k.channel().close(); } selector.close(); return; } } else { key.cancel(); } break; default: System.out.println("unhandled " + key.readyOps()); break; } } } } 

ObsSelectionKeyOP_ACCEPT …)的字段是静态导入的:

 import static java.nio.channels.SelectionKey.*; 

许多框架,例如Apache MINA和Netty ,都是基于Java NIO实现的,以增强非阻塞IO编程。 我强烈建议他们让你的NIO编程变得快乐,而不是噩梦。 它们适合你的问题。

此外,尝试在传输消息大小和编码/解码(序列化/反序列化)性能中使用有效的协议。 Google Protocol Buffers是该领域的可靠解决方案。 还可以看看Kryo和KryoNet 。 他们可以提供帮助。