【问题标题】:Non-blocking server using Java NIO [closed]使用 Java NIO 的非阻塞服务器 [关闭]
【发布时间】:2012-06-01 10:37:23
【问题描述】:

我正在使用this 教程构建一个没有可写部分的 java nio 服务器。

一切正常,除了一件有趣的事情:

  • 当客户端发送数据包速度过快时,服务器不会收到所有消息,服务器总是会收到第一个和第二个数据包,但不会超过。
  • 如果客户端发送数据包的速度很慢,服务器会获取所有数据包。

有什么想法吗?

我正在添加服务器类代码,如果您需要下面代码中提到的另一个类,我在这里:)。

NIOServer 类:

package server;
import java.io.IOException;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.nio.channels.spi.SelectorProvider;
import java.util.*;

import javax.xml.parsers.ParserConfigurationException;

import org.xml.sax.SAXException;

public class NioServer implements Runnable {



// The host:port combination to listen on
  private InetAddress hostAddress;
  private int port;

  // The channel on which we'll accept connections
  private ServerSocketChannel serverChannel;

  // The selector we'll be monitoring
  private Selector selector;

  //the cach will hundle the messages that came
  private Cache cache;

  // The buffer into which we'll read data when it's available
  private ByteBuffer readBuffer = ByteBuffer.allocate(8192);

  public NioServer(InetAddress hostAddress, int port , Cache cache) throws IOException {
    this.cache = cache;
    this.hostAddress = hostAddress;
    this.port = port;
    this.selector = this.initSelector();
  }


  private Selector initSelector() throws IOException {
        // Create a new selector
        Selector socketSelector = SelectorProvider.provider().openSelector();

        // Create a new non-blocking server socket channel
        this.serverChannel = ServerSocketChannel.open();
        serverChannel.configureBlocking(false);

        // Bind the server socket to the specified address and port
        InetSocketAddress isa = new InetSocketAddress(this.hostAddress, this.port);
        serverChannel.socket().bind(isa);

        // Register the server socket channel, indicating an interest in 
        // accepting new connections
        serverChannel.register(socketSelector, SelectionKey.OP_ACCEPT);

        return socketSelector;
      }

  private void accept(SelectionKey key) throws IOException {
        // For an accept to be pending the channel must be a server socket channel.
        ServerSocketChannel serverSocketChannel = (ServerSocketChannel) key.channel();

        // Accept the connection and make it non-blocking
        SocketChannel socketChannel = serverSocketChannel.accept();
        Socket socket = socketChannel.socket();
        socketChannel.configureBlocking(false);

        // Register the new SocketChannel with our Selector, indicating
        // we'd like to be notified when there's data waiting to be read
        socketChannel.register(this.selector, SelectionKey.OP_READ);
      }

  private void read(SelectionKey key) throws IOException {
        SocketChannel socketChannel = (SocketChannel) key.channel();

        // Clear out our read buffer so it's ready for new data
        this.readBuffer.clear();

        // Attempt to read off the channel
        int numRead;
        try {
          numRead = socketChannel.read(this.readBuffer);
          String test = new String(this.readBuffer.array());
          System.out.print(test);

        } catch (IOException e) {
          // The remote forcibly closed the connection, cancel
          // the selection key and close the channel.
        //  key.cancel();
        //  socketChannel.close();
          return;
        }

        if (numRead == -1) {
          // Remote entity shut the socket down cleanly. Do the
          // same from our end and cancel the channel.
          key.channel().close();
          key.cancel();
          return;
        }

        // Hand the data off to our worker thread
        this.cache.processData(this, socketChannel, this.readBuffer.array(), numRead); 
      }

  public void run() {
        while (true) {
          try {
            // Wait for an event one of the registered channels

            this.selector.select();



            // Iterate over the set of keys for which events are available
            Iterator selectedKeys = this.selector.selectedKeys().iterator();
            while (selectedKeys.hasNext()) {
              SelectionKey key = (SelectionKey) selectedKeys.next();
              selectedKeys.remove();

              if (!key.isValid()) {
                continue;
              }

              // Check what event is available and deal with it
              if (key.isAcceptable()) {
                this.accept(key);
              } else if (key.isReadable()) {
                this.read(key);
              }
            }
          } catch (Exception e) {
            e.printStackTrace();
          }
        }
      }

  public static void main(String[] args) throws ParserConfigurationException, SAXException {
    try {
        Cache cache = new Cache();
        new Thread(cache).start();
      new Thread(new NioServer(null, 9090,cache)).start();
    } catch (IOException e) {
      e.printStackTrace();
    }
  }

【问题讨论】:

  • 您的代码中一定有错误。如果您需要更多帮助,请向我们提供更多信息。
  • TCP 不会丢失数据,NIO 也不会。您要么没有读取所有数据,要么将其中的一些数据丢弃。没有一些代码可以评论,就不可能进一步评论。
  • 1.如果在读取时捕获 IOException,则必须 (a) 记录它并 (b) 关闭通道。 2.关闭e频道后无需取消key。
  • 注意 1. 这是一个非阻塞服务器,而不是异步服务器。 2. ROX NIO 教程在这个问题发布之前很多年都被抹黑了。我已经在 Sun/Oracle Java 论坛上写了大量关于它的文章。它包含许多事实错误和糟糕的编程。
  • 这个问题应该被关闭,因为 OP 在八年内没有提供任何发送代码和证据,应该恢复@BillTheLizard 我对它的正确答案。

标签: java tcp nio


【解决方案1】:

如果您正在阅读 UDP,我希望如此。请注意您在 read 方法上处理数据包的速度有多慢。您正在将它们打印到 system.out,这非常慢,而且不确定您能够以多快的速度将数据处理到 processData 方法上的另一个线程。 This library 我写的可以帮助你进行线程间非阻塞通信,如果这是你滞后的根源。您还应该检查底层读取套接字缓冲区的大小。它越大,在数据包开始被丢弃之前,您必须有更多的空间快速赶上。对于 TCP,如果底层套接字缓冲区已满,您可能会在通道上收到 IOException。对于 UDP,数据包会被静默丢弃。

要访问底层的读取套接字缓冲区大小,您可以这样做:

final Socket socket = channel.socket();
System.out.println(socket.getReceiveBufferSize());
socket.setReceiveBufferSize(newSize);

注意:据我所知,Linux 可能需要一些操作系统配置才能更改底层缓冲区大小。如果setReceiveBufferSize 无效(再次阅读以查看是否已更改),请谷歌搜索。 :)

【讨论】:

  • 这是一个 TCP 服务器。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2022-12-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-01-01
  • 1970-01-01
  • 2011-03-20
相关资源
最近更新 更多