Search code examples
socketstcpbuffervert.x

Buffer is merged with another Buffer in TCP Vert.x server


I would like to send commands to server via TCP. I created server which retrieve Buffers messages. In client I send 10 commands. I don't know why server reads Buffer in not predictable way.

server:

@Slf4j
public class TcpServ {

    public static void main(String[] args) {
        Vertx vertx = Vertx.vertx();
        NetServer server = vertx.createNetServer();

        server.connectHandler(socket -> {
            log.debug("New connection established with: socket address {}", socket.remoteAddress());

            socket.handler(new MyHandler());

            socket.closeHandler(v -> {
                log.debug("The socket has been closed.");
            });
        });

        server.listen(8088, "localhost", netServerAsyncResult -> {
            if (netServerAsyncResult.succeeded()) {
                log.debug("Server is now listening on actual port: " + server.actualPort());
            } else {
                log.debug("Failed to bind!");
            }
        });
    }

    @Slf4j
    static class MyHandler implements Handler<Buffer> {

        @Override
        public void handle(Buffer event) {
            byte[] bytes = event.getBytes();
            log.debug("Received data: {}", new String(bytes));
        }

    }

}

client:

@Slf4j
public class TcpClie {

    public static void main(String[] args) {
        Vertx vertx = Vertx.vertx();

        NetClient client = vertx.createNetClient();

        client.connect(8088, "localhost", netSocketAsyncResult -> {

            log.debug("Connection established.");

            NetSocket socket = netSocketAsyncResult.result();

            for (int j = 0; j < 10; j++) {
                String s = "Command" + j;
                Buffer b = Buffer.buffer(s.getBytes());
                socket.write(b);
            }

            log.debug("Data sent.");

            socket.handler(buffer -> {
                log.debug("Recived data from server with size: " + buffer.length());
            });

        });

    }

}

What I got is once:

[vert.x-eventloop-thread-1] DEBUG TcpServ$MyHandler - Received data: Command0Command1Command2Command3Command4Command5Command6Command7Command8Command9

another time is:

[vert.x-eventloop-thread-1] DEBUG TcpServ$MyHandler - Received data: Command0
[vert.x-eventloop-thread-1] DEBUG TcpServ$MyHandler - Received data: Command1
[vert.x-eventloop-thread-1] DEBUG TcpServ$MyHandler - Received data: Command2
[vert.x-eventloop-thread-1] DEBUG TcpServ$MyHandler - Received data: Command3
[vert.x-eventloop-thread-1] DEBUG TcpServ$MyHandler - Received data: Command4Command5
[vert.x-eventloop-thread-1] DEBUG TcpServ$MyHandler - Received data: Command6Command7
[vert.x-eventloop-thread-1] DEBUG TcpServ$MyHandler - Received data: Command8
[vert.x-eventloop-thread-1] DEBUG TcpServ$MyHandler - Received data: Command9

What I expect is every time I use socket.write() server recive Buffer. Expected output should be:

[vert.x-eventloop-thread-1] DEBUG TcpServ$MyHandler - Received data: Command0
[vert.x-eventloop-thread-1] DEBUG TcpServ$MyHandler - Received data: Command1
[vert.x-eventloop-thread-1] DEBUG TcpServ$MyHandler - Received data: Command2
[vert.x-eventloop-thread-1] DEBUG TcpServ$MyHandler - Received data: Command3
[vert.x-eventloop-thread-1] DEBUG TcpServ$MyHandler - Received data: Command4
[vert.x-eventloop-thread-1] DEBUG TcpServ$MyHandler - Received data: Command5
[vert.x-eventloop-thread-1] DEBUG TcpServ$MyHandler - Received data: Command6
[vert.x-eventloop-thread-1] DEBUG TcpServ$MyHandler - Received data: Command7
[vert.x-eventloop-thread-1] DEBUG TcpServ$MyHandler - Received data: Command8
[vert.x-eventloop-thread-1] DEBUG TcpServ$MyHandler - Received data: Command9

Solution

  • This is not a bug. TCP is a low-level protocol, it does not define what is a request or command like HTTP does.

    You must define your own application protocol on top of TCP, which can be as simple as adding the newline \n character between them, or having a fixed buffer size for all commands.

    On the server side, you could then use the Vert.x RecordParser:

    RecordParser parser = RecordParser.newDelimited("\n", h -> {
      System.out.println(h.toString());
    });
    
    parser.handle(Buffer.buffer("command0\ncomma"));
    parser.handle(Buffer.buffer("nd1\nco"));
    parser.handle(Buffer.buffer("mmand2"));
    

    Will result in:

    command0
    command1
    command2