Patze
Patze

Reputation: 307

Read bytes from Java NIO socketchannel until marker is reached

I´m searching for an efficient way to read bytes from a socket channel using Java NIO. The task is quite easy, I have a solution, though I´m searching for a cleaner and more efficient way to solve this. Here´s the scenario:

  1. Data is read from a socket channel
  2. This data is a UTF-8 encoded string
  3. Every line is ended by \r\n, the length is unknown up front
  4. After every line read, I want to do something with the message

My solution reads the data byte per byte and compares every byte to my marker (which is has the value 10 in UTF-8 code pages). Here´s the code:

ByteBuffer res = ByteBuffer.allocate(512);
boolean completed = false;
try {
    while (true) {
        ByteBuffer tmp = ByteBuffer.allocate(1);
        if(soc.read(tmp) == -1) {
             break;
        }

        // set marker back to index 0
        tmp.rewind();
        byte cur = tmp.get();
        res.put(cur);

        // have we read newline?
        if (cur == 10) {
            doSomething(res);
            res.clear();
        }
    }

} catch(Exception ex) {
     handle(ex);
}

Even though this does the job, there might be a better way, that doesn't need those per byte comparisons after every iteration.

Thanks for your help!

Upvotes: 7

Views: 4625

Answers (2)

Patze
Patze

Reputation: 307

Here´s the code of my final solution.

ByteBuffer res = ByteBuffer.allocate(maxByte);
while (true) {
    ByteBuffer tmp = ByteBuffer.allocate(maxByte);

    int bytesRead = clientSocket.read(tmp);
    if (bytesRead == -1) {
        break;
    }

    // rewind ByteBuffer to get it back to start
    tmp.rewind();

    for (int i = 0; i < bytesRead; i++) {
        byte cur = tmp.get(i);
        res.put(cur);
        if (cur == marker) {
            processMessage(res);
            res = ByteBuffer.allocate(maxByte);
        }
    }

    // reached end of message, break loop
    if (bytesRead < tmpSize) {
        break;
    }
}

Upvotes: 0

Peter Lawrey
Peter Lawrey

Reputation: 533442

The way I would do it is to read as much as is available, such as 32 KB, and once you have read this, you copy the data byte-by-byte to another buffer, e.g. a StringBuilder. If there is data left in the buffer from the last time you read, you can continue using the buffer until it it is all consumed, at which point you read more data.

Note: each system call is expensive. It could take 2-5 micro-seconds. This doesn't sound like much unless you call it millions of times and it will add seconds to reading 1 MB.

Upvotes: 5

Related Questions