简体   繁体   English

我可以使用Java NIO从Socket读取可用字节吗?

[英]can I get available bytes to read from a Socket using Java NIO?

I want to know if there are available bytes to be read in a Java NIO Socket. 我想知道在Java NIO套接字中是否有可读字节。 In C/C++ it can be done using something like this: 在C / C ++中,可以使用以下内容完成:

int available(int fd){
  long readable = 0;

    if (ioctl(fd, FIONREAD, &readable) < 0){
      // error
    }

  return (int) readable;
}

It is possible to do the same operation with Java NIO (using SocketChannel , Selector , Socket , etc.)? 可以使用Java NIO (使用SocketChannelSelectorSocket等)执行相同的操作吗?

Can't be done. 无法做到。 An API is present, formally speaking, via SocketChannel.socket().getInputStream().available() , but getInputStream() operations will fail on a non-blocking channel, so it can't be used in your circumstance. 正式来说,API通过SocketChannel.socket().getInputStream().available() ,但getInputStream()操作将在非阻塞通道上失败,因此它不能在您的环境中使用。

EDIT: Now that you've illuminated us a little, what you require still doesn't exist in Java, but as you are in non-blocking mode it doesn't matter in the slightest. 编辑:既然你已经照亮了我们一点点,你所需要的东西在Java中仍然不存在,但是当你处于非阻塞模式时,它无关紧要。 Just read into a buffer that is at least as big as your socket receive buffer: the number of bytes read is the number of bytes that could have been read without blocking, which is exactly what you just did. 只需读入一个至少与套接字接收缓冲区一样大的缓冲区:读取的字节数是可以在不阻塞的情况下读取的字节数,这正是您刚才所做的。

The idea behind NIO is to provide a way to wait for events (eg "connection has readable data ready") from any one of multiple connections at the same time, which sounds like exactly what you are looking for. NIO背后的想法是提供一种方法来同时从多个连接中的任何一个连接等待事件(例如“连接已准备好可读数据”),这听起来就像您正在寻找的那样。 Take a look at java.nio.channels.Selector . 看看java.nio.channels.Selector You basically register the "readable" event of all the connections you have with this selector object and then call one of three select methods that will wait for an event on one of your connections: 您基本上使用此选择器对象注册所有连接的“可读”事件,然后调用将等待其中一个连接上的事件的三个select方法之一:

  1. select() - blocks until an event is available (use if your program has nothing else to do) select() - 阻塞直到事件可用(如果你的程序没有别的事情可以使用)
  2. select(long timeout) - blocks until an event is available or a timeout happens (use if you want to conserve CPU load and increase network responsiveness if it's OK if your program slows down a bit) select(long timeout) - 阻塞直到事件可用或发生超时(如果你想节省CPU负载并提高网络响应性,如果你的程序运行速度慢了就可以使用)
  3. selectNow() - returns immediately (use if your program needs to keep running) selectNow() - 立即返回(如果你的程序需要继续运行,请使用)

Then you use the selectedKeys method to grab a list of all connections that have an event waiting (for example, have data ready to be read). 然后使用selectedKeys方法获取具有等待事件的所有连接的列表(例如,准备好读取数据)。 Then just iterate over this list and read from the connections with data only and ignore the other connections that aren't in the list as they have no data available. 然后只需遍历此列表并从仅包含数据的连接中读取,并忽略列表中没有的其他连接,因为它们没有可用的数据。

This will allow you to check without blocking WHETHER data is available (is connection in the list or not), but not HOW MUCH data is available. 这将允许您无阻塞地检查数据是否可用(列表中是否有连接),但不能获得多少数据。 But if you then do a read on the connection with data available, it will return immediately without blocking and return as much data as is available, if you've given it a big enough buffer. 但是,如果您随后读取有关可用数据的连接,它将立即返回而不会阻塞,并返回尽可能多的数据,如果您已经给它足够大的缓冲区。 You can then choose to buffer this data somewhere and make the amount of data in the buffer available, but something tells me you don't really need this anyways and just want to go ahead and process the data. 然后,您可以选择在某处缓冲此数据并使缓冲区中的数据量可用,但有些事情告诉我您不需要这样做,只是想继续处理数据。

I have read tons of documentation, forums and, obviously each of your responses. 我已经阅读了大量文档,论坛以及您的每个回复。 I have developed and tested a solution. 我开发并测试了一个解决方案。 It is not the best, but it is my best approximation. 它不是最好的,但它是我最好的近似值。

I use selectNow() method of Selector . 我使用Selector的 selectNow()方法。 Given a Selector named selector that have a SocketChannel registered, I can use this method to know if the associated Socket has readable bytes: 给定一个名为selector已注册SocketChannelSelector ,我可以使用此方法来了解关联的Socket是否具有可读字节:

public boolean isReadable() {
    try {
        selector.selectNow();
        Set<SelectionKey> selectedKeys = selector.selectedKeys();
        Iterator<SelectionKey> keyIterator = selectedKeys.iterator();
        while(keyIterator.hasNext()) {
            SelectionKey selectionKey = keyIterator.next();
            if(!selectionKey.isValid()) {
                continue;
            }
            if(selectionKey.isReadable()) {
                return true;
            }
            keyIterator.remove();
        }
    } catch(IOException e) {
        // error
    }
    return false;
}

It is the only way I have found to know if a Socket has readable bytes without reading the bytes. 这是我发现知道Socket是否具有可读字节而不读取字节的唯一方法。 Do you know a better way to do it or disadvantages of doing it by this way? 您是否知道更好的方法或通过这种方式做到这一点的缺点?

EDIT: 编辑:

This solution works the first time, but it has a drawback: it always returns true even if 0 bytes are readable. 这个解决方案第一次工作,但它有一个缺点:即使0字节是可读的,它总是返回true。 A socket can be readable even if no data is available. 即使没有可用的数据,套接字也是可读的。 This is not the behaviour I want, because I don't want to read when there is no data in the read buffer. 这不是我想要的行为,因为当读缓冲区中没有数据时我不想读取。

EDIT2 BIS: EDIT2 BIS:

Test this code: 测试此代码:

public static boolean isReadable(Selector selector) {
    try {
        selector.selectNow();
        Set<SelectionKey> selectedKeys = selector.selectedKeys();
        Iterator<SelectionKey> keyIterator = selectedKeys.iterator();
        while (keyIterator.hasNext()) {
            SelectionKey selectionKey = keyIterator.next();
            if (!selectionKey.isValid()) {
                continue;
            }
            if (selectionKey.isReadable()) {
                    keyIterator.remove();
                return true;
            }
            keyIterator.remove();
        }
    } catch (IOException e) {
        System.err.println("Error!");
        e.printStackTrace();
    }
    return false;
}

public static void read() {
    try {
        Selector selector = Selector.open();
        SocketChannel socketChannel = SocketChannel.open();

        Socket socket = socketChannel.socket();

        System.out.println("Connecting to server...");
        socket.connect(new InetSocketAddress("localhost", 9999));
        System.out.println("Connected to server.");

        socketChannel.configureBlocking(false);

        socketChannel.register(selector, SelectionKey.OP_READ);

        ByteBuffer bb = ByteBuffer.allocate(1024);

        while (true) {
            boolean readable = isReadable(selector);
            if (readable) {
                System.out.println("Readable data found! Let's read...");
                bb.clear();
                int readBytes = socketChannel.read(bb);
                if (readBytes < 0) {
                    System.out.println("End of Stream found");
                    break;
                }
                byte[] readArray = new byte[readBytes];
                System.arraycopy(bb.array(), 0, readArray, 0, readBytes);
                System.out.println("Read (" + (readBytes) + " bytes): "
                        + new String(readArray, Charset.forName("UTF-8")));
                bb.flip();
                socketChannel.write(bb);
                Thread.sleep(1000);
            } else {
                System.out.println("No data to read, sleeping");
                Thread.sleep(1000);
            }
        }
    } catch (Exception e) {
        e.printStackTrace();
    }
}

public static void main(String[] args) {
    read();
}

First of all, I execute an application that listens for connections and writes data: I used netcat with this command: nc -l 9999 . 首先,我执行一个监听连接和写入数据的应用程序:我使用netcat和这个命令: nc -l 9999 Then I execute my Java program. 然后我执行我的Java程序。 Finally I can write text lines in netcat and my Java program works perfectly. 最后,我可以在netcat编写文本行,我的Java程序也能完美运行。

Remember it is a proof of concept, not a way to real programming solution. 请记住,它是一个概念证明,而不是真正的编程解决方案。 In fact, it is a very bad practise. 事实上,这是一个非常糟糕的做法。 Thank you @EJP and @Markus! 谢谢@EJP和@Markus!

I'm not aware of any method in NIO that does that. 我不知道NIO中有任何方法可以做到这一点。 One just go ahead and read. 一个人就去吧。

Bytebuffer.limit will return you the length available in the buffer. Bytebuffer.limit将返回缓冲区中可用的长度。

sctpChannel.receive(byteBuffer, null, null);
byteBuffer.flip();

 if (byteBuffer.limit() > 0) 
  {
         .....................//Do your work here
    //             read the data in a byte array
    byteBuffer.clear();  
  }

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM