Java NIO Socketchannel отправляет данные только после выключения вывода.Почему это? - PullRequest
0 голосов
/ 04 апреля 2019

Итак, я пытаюсь написать программу, которая принимает java nio SocketChannel Connections, но также сохраняет их открытыми. Допустим, Клиент отправляет сообщение, и если Сервер немедленно отправит то же сообщение обратно (Simple Echo Server), но процесс ответа не будет работать, если Клиент отправит новое сообщение, Сервер не ответит, пока я не закрою сокетный канал Клиента. , Но после того, как я закрыл канал Клиента, все отправленные ранее сообщения будут приходить сразу. (Извините за мой ломаный английский, это не мой родной язык)

Процесс записи используется на стороне сервера и клиента.

Процесс написания:

                    try {
                        final ByteBuffer byteBuffer = ByteBuffer.wrap(data);
                        while(byteBuffer.hasRemaining()){
                            socketChannel.write(byteBuffer);
                        }
                        byteBuffer.flip();
                    } catch (IOException exception) {
                        throw new BloumException(exception.getMessage());
                    }

Процесс чтения:

final ByteBuffer byteBuffer = ByteBuffer.allocate(DefaultConnectionCreator.this.getDefaultBufferSize());

                try {
                    while(socketChannel.read(byteBuffer) != -1){    
                        //byteBuffer.clear();
                    }
                } catch (IOException exception) {
                    exception.printStackTrace();
                    throw new BloumException(exception.getMessage());
                }
                return byteBuffer.array();

Процесс выбора ключа (автоматическое закрытие возвращает false):

private void handleKeys(final ServerSocketChannel serverSocketChannel, Set<SelectionKey> keys, HashMap<SocketChannel, ByteBuilder> sessions) throws Exception{
        final Iterator<SelectionKey> iterator = keys.iterator();
        while(iterator.hasNext()){
            final SelectionKey selectionKey = iterator.next();
            iterator.remove();
            if(selectionKey.isValid()){
                if(selectionKey.isAcceptable()){
                    final ServerSocketChannel serverSocketChannel2 = (ServerSocketChannel)selectionKey.channel();
                    final SocketChannel socketChannel = serverSocketChannel2.accept();
                    socketChannel.configureBlocking(false);
                    socketChannel.register(selectionKey.selector(), SelectionKey.OP_READ);
                    sessions.put(socketChannel, new ByteBuilder());

                }else if(selectionKey.isReadable()){
                    final SocketChannel socketChannel = (SocketChannel)selectionKey.channel();
                    final ByteBuffer byteBuffer = ByteBuffer.allocate(Connection.MAX_BUFFER_SIZE);

                    final Integer bytesRead = socketChannel.read(byteBuffer);
                    if(bytesRead!=-1){
                        byte[] data = new byte[bytesRead];
                        System.arraycopy(byteBuffer.array(), 0, data, 0, bytesRead);
                        Boolean autoClose = true;
                        if(ConnectionHost.this.handle(this.getConnectionCreator().createConnection(socketChannel), data)){
                            autoClose=true;
                        }else autoClose=false;


                        if(autoClose){
                            sessions.remove(socketChannel);
                            socketChannel.close();
                        }else{
                            if(!sessions.containsKey(socketChannel))sessions.put(socketChannel, new ByteBuilder());
                        }
                    }
                }else throw new BloumException("The given key is not supported.");
            }else throw new BloumException("The key is not valid anymore.");

        }
    }

Ответы [ 2 ]

1 голос
/ 07 апреля 2019

Здравствуйте, у вас много ошибок там

  1. Вам нужно перевернуть буфер перед записью
  2. Если чтение равно -1, вы ДОЛЖНЫ закрыть канал
  3. Не создавайте 1-гигабайтный буфер, используемые размеры 256, 1024, 4096, 8196, я рекомендую 4096.
  4. Используйте прямые буферы, прямая память зарекомендовала себя быстрее для ввода-вывода, посколькуон не прерывается сборщиком мусора
  5. Не иметь оператора if для логического значения, установить логическое значение равным оператору
  6. Не иметь цикла while, игнорирующего прочитанные байты, если он0 означает, что вам нечего читать, что заставляет вас зацикливаться до тех пор, пока сокет не закроется
  7. Я почти уверен, что ваш цикл для удаления 0 - это удаление данных, которые не были добавлены, но вы можете простосделать Arrays.copyOfRange(buffer.array(), 0, buffer.position()) или если вы щелкнули буфером, тогда Arrays.copyOfRange(buffer.array(), 0, buffer limit())
0 голосов
/ 04 апреля 2019

Проблема была на самом деле довольно простой, после небольшой попытки я наконец нашел решение.Я забыл добавить заявление о перерыве в процессе чтения.Я также добавил простой алгоритм, который удаляет 0-байтовые байты, вызванные байтовым буфером 1 ГБ.

final ByteBuffer byteBuffer = ByteBuffer.allocate(DefaultConnectionCreator.this.getDefaultBufferSize());

                try {
                    while(socketChannel.read(byteBuffer) != -1){    
                        //byteBuffer.clear();
                    }
                } catch (IOException exception) {
                    exception.printStackTrace();
                    throw new BloumException(exception.getMessage());
                }
                final ArrayList<Byte> bytes = new ArrayList<>();

                Integer current = 0;

                for(Integer i = 0; i<byteBuffer.array().length; i++){
                    final byte b = byteBuffer.array()[i];
                    if(b==0){
                        Boolean okay = false;
                        for(Integer x = i+1; x<byteBuffer.array().length; x++){
                            if(byteBuffer.array()[x]!=0){
                                okay=true;
                                break;
                            }
                        }
                        if(!okay){
                            break;
                        }
                        bytes.add(b);
                    }else bytes.add(b);

                    current=current+1;
                }
                return this.toPrimitive(bytes.toArray(new Byte[bytes.size()]));

Метод toPrimitive:

/**
* Converts a integer array to a integer Integer array.
* @param doubleArray
* @return the primitive integer array.
*/
private byte[] toPrimitive(Byte[] byteArray) {
    byte[] result = new byte[byteArray.length];
    for (int i = 0; i < byteArray.length; i++) {
        result[i] = byteArray[i];
    }
    return result;
}
...