在NIO

时间:2017-06-03 18:46:59

标签: java sockets nio bytebuffer

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

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

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

有时响应有这样奇怪的尾巴:{"class":"server.PasswordHashResponse","xoredHash":"RV5GX1JVAwADBEVZWwFGTAhZQ1FGX1tYQ11ZVwA\u003d"}YQ11ZVwA\u003d

server和cliend都使用相同的方法进行读写。 我从客户{"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();
}

1 个答案:

答案 0 :(得分:2)

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

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