Search code examples
javasocketsioniosocketchannel

Java NIO Socketchannel only sends data after shutting down output. Why is this?


I am trying to write a program which accepts java nio SocketChannel Connections but also keeps them open. So lets say the Client sends a message, then should the Server immediately send the same message back (Simple Echo Server), but the answering process does not work. If the Client sends a new message the Server will not respond until I closed the Client's socketchannel. But after I closed the Client's channel all the previously sent Messages will come in one go. (Sorry for my broken English, it is not my native language)

The writing process is used for Server and Client side.

Writing process:

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

Reading process:

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();

Key selection process (Auto close Returns 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.");
            
        }
    }

Solution

  • Hello you have a lot of mistakes there

    1. You need to flip the buffer before writing
    2. If read is -1 you MUST close the channel
    3. Don't create a 1GB byte buffer the used sizes are 256, 1024, 4096, 8196 I recommend 4096.
    4. Use direct buffers, direct memory has proven itself faster for I/O since it is not interrupted by the garbage collector
    5. Don't have an if statement for a boolean set the boolean equal to the statement
    6. Don't have a while loop ignoring read bytes, if it is 0 it means you don't have anything to read causing you to loop until the socket has closed
    7. I am pretty sure your loop there to remove 0's is to remove data that wasn't added but you can just do Arrays.copyOfRange(buffer.array(), 0, buffer.position()) or if you flipped the buffer then Arrays.copyOfRange(buffer.array(), 0, buffer limit())