java - NIO读写的很多问题

标签 java sockets nio bytebuffer

我正在使用 NIO 进行练习,并尝试使用客户端和服务器端制作一个简单的应用程序。这个应用程序应该只是从客户端向服务器发送字节消息,并获取其他消息作为响应。我的代码在下面。但我有很多不同的问题。

有时来自方法 readMessage() 的行 int bytesRead = socketChannel.read(byteBuffer);bytesRead = socketChannel.read(byteBuffer); > 读取无限的零字节序列并抛出 OOM 错误。

有时来自服务器的响应看起来像 that而不是 {"class":"server.PasswordHashResponse","xoredHash":"RV5GX1JVAwADBEVZWwFGTAhZQ1FGX1tYQ11ZVwA\u003d"}

有时响应有奇怪的尾部,如下所示:{"class":"server.PasswordHashResponse","xoredHash":"RV5GX1JVAwADBEVZWwFGTAhZQ1FGX1tYQ11ZVwA\u003d"}YQ11ZVwA\u003d

服务器和客户端都使用相同的方法进行读写。 我从客户端发送 {"class":"server.PasswordHashRequest","login":"admin"} 并期望 {"class":"server.PasswordHashResponse","xoredHash":"RV5GX1JVAwADBEVZWwFGTAhZQ1FGX1tYQ11ZVwA\u003d"}。 使用相同的代码,我现在可以得到一个问题,几分钟后又可以得到另一个问题。 我已经尝试过我所知道的一切。我是否在 Java 中遇到了段错误?

客户端代码:

    @Test
public void main() throws Exception {
    System.out.println("Opening socket");
    InetSocketAddress socketAddress = new InetSocketAddress("localhost", 9090);
    SocketChannel socketChannel = SocketChannel.open();
    socketChannel.configureBlocking(false);
    Selector selector = Selector.open();
    socketChannel.register(selector, OP_CONNECT);
    socketChannel.connect(socketAddress);
    PasswordHashRequest request = new PasswordHashRequest("admin");
    System.out.println("Socket open");
    while (true) {
        System.out.println("Client selector awoken");
        selector.select();
        for (SelectionKey selectionKey : selector.selectedKeys()) {
            if (selectionKey.isConnectable()) {
                socketChannel.finishConnect();
                selectionKey.interestOps(OP_WRITE);
            } else if (selectionKey.isReadable()) {
                String response = ServerManager.readMessage((SocketChannel) selectionKey.channel());
                System.out.println(response);
                server.interrupt();
            } else if (selectionKey.isWritable()) {
                ServerManager.sendMessage(request, (SocketChannel) selectionKey.channel());
                System.out.println("Request sent");
                selectionKey.interestOps(OP_READ);
            }
        }
    }
}

服务器端代码:

    public void run() {
    System.out.println("Main thread started");
    while (true) {
        try {
            // Get ready channels
            int readyChannels = selector.select();
            if (readyChannels == 0) { continue; }

            Set<SelectionKey> selectedKeys = selector.selectedKeys();
            Iterator<SelectionKey> keyIterator = selectedKeys.iterator();

            // Handle Events
            while (keyIterator.hasNext()) {
                SelectionKey key = keyIterator.next();

                // New Client
                if (key.isAcceptable()) {
                    System.out.println("New Client Accepted");
                    ServerSocketChannel serverSocketChannel = (ServerSocketChannel) key.channel();
                    serverSocketChannel.configureBlocking(false);

                    SocketChannel socketChannel = serverSocketChannel.accept();
                    socketChannel.configureBlocking(false);
                    SelectionKey clientKey = socketChannel.register(selector, SelectionKey.OP_READ);
                    Random randomInt = new Random(System.currentTimeMillis());
                    clientKey.attach(randomInt.nextInt(Integer.SIZE - 1));
                }
                // Client has sent data
                else if (key.isReadable()) {
                    handleInput(key);
                }

                keyIterator.remove();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

读取方法:

    public static String readMessage(SocketChannel socketChannel) throws IOException {
    ByteBuffer byteBuffer = ByteBuffer.allocate(16);
    byteBuffer.clear();
    StringBuilder stringBuilder = new StringBuilder();
    int bytesRead = socketChannel.read(byteBuffer);
    while (bytesRead != -1) {
        byteBuffer.flip();
        String byteString = new String(byteBuffer.array(), Charset.forName("UTF-8"));
        stringBuilder.append(byteString);
        byteBuffer.clear();
        bytesRead = socketChannel.read(byteBuffer);
    }
    socketChannel.shutdownInput();
    return stringBuilder.toString();
}

写入方法:

    public static void writeMessage(String message, SocketChannel channel) throws IOException {
    message += "\r\n";
    System.out.println(message);
    int bufferLength = 16;
    byte[] responseBytes = message.getBytes();
    int offset = 0;
    ByteBuffer buf = ByteBuffer.allocate(bufferLength);
    while (responseBytes.length > offset) {
        buf.clear();
        int div = responseBytes.length - offset;
        if (div >= bufferLength) {
            buf.put(responseBytes, offset, bufferLength);
        } else {
            buf.put(responseBytes, offset, div);
        }
        buf.flip();
        channel.write(buf);
        offset += bufferLength;
    }
    channel.shutdownOutput();
}

最佳答案

  • 如果 bytesRead <= 0,您的读取方法应该停止读取
  • 构造字符串时应考虑缓冲区限制
  • 如果write(),您的写入方法应该停止尝试写入返回零,然后(并且只有那时)为 OP_WRITE 注册 channel ,并且仅在触发时继续写入。

在这里看到许多类似的问题。

关于java - NIO读写的很多问题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/44347420/

相关文章:

java - 如何从文件夹路径创建文件路径

java - 使用java离线语音转文本

java - 是否需要不基于 Spring/Tomcat 的 Java 7 云服务器框架?

java - 编写一个类,以便它可以扩展或传递 block JAVA

我可以将父进程的 stderr 重定向到 fork 进程上的套接字文件描述符吗?

C/确定接收数据包的端口

Java在主线程中监听套接字时读取线程中的控制台输入

Java7 WatchService - 尝试删除递归监视的嵌套目录时出现拒绝访问错误(仅限 Windows)

java - Protobuf Java - 不区分大小写的 map ?

java - 使用 JavaConfig 示例的 Spring Security Digest Auth