0

I'm using a java server to connect to a browser with secure websockets. All works fine with the connect, but many times i get an unexpected -1 result from socket.in.read(buffer,off,len), this happens also in the middle of a frame. Normally i close a socket directly upon reception of -1, since it is end of stream. However i noted that it can also happen on a connection reset. I have come over many cases where in my tests the socket whould return valuable data after read returned -1. I even have the feeling this is more often than not. My problem arrises when sometimes i just get some scrambled data out of the socket after such a case. Another problem is that the other side is not notified when a frame cannot be delivered... So what good is TCP/SSL than? if you need to consider it an unreliable connection for transporting websocket frames in java?

I have some schemes to use that are used to deal with unreliable connections for making shure a packet arrives. But i hope that somebody knows what to do after read returns -1.

Sorry for the somewhat vague description in this one... i'm getting tired with solving this issue.

Just an example of some rubbish comming in (only text frames are submitted containing JSON data):

16-06-13 22:43:13.918;WebSocket;7: Read frame from websocket: 377, opcode:UNKNOWN
data: null
16-06-13 22:43:13.918;WebSocket;7: Read frame from websocket: 377, opcode:PONG_FRAME
data: null
16-06-13 22:43:13.918;WebSocket;7: Read frame from websocket: 377, opcode:TEXT_FRAME
data: =,6GiGGV7C6_TfPHg\~\c

Here another example of a received frame that is just a bit malformed!? how is this possible with a TCP/TLS connection???:

17-06-13 09:42:37.510;WebSocket;7: Read frame from websocket: 15, opcode:TEXT_FRAME
data: "kep-aiveY:"d613Nb2-N24eV463K-808-fJb30I9e3M02

It is supposed to read {"keep-alive":"[UUID]"}

Meanwhilst i have done some more testing and found that 9 out of 10 times it works if you continue reading after reception of -1. So even if you are reading halfway the frame and receive a -1 then you should test somehow if the socket is closed or not, i now use: socket.isInputShutdown(). if this is not the case then just continue filling up the buffer. To do so i now use the following code where socket is the SSLSocket:

public static int readFully(Socket socket, InputStream is, byte[] buffer, int off, int len) throws IOException
{
    int read = 0;
    while(read < len)
    {
        int b = is.read();
        if(b < 0)
        {
            Logger.log(TAG, "readFully read returned: " + b + " testing if connection is reset or closed.", Logger.WARNING);
            if(socket.isInputShutdown())
            {
                throw new IOException("InputStream closed before data could be fully read! (readFully read returned -1 and socket.isInputShutdown() is true");
            }
        }
        else
        {
            buffer[off + (read++)] = (byte) b;
        }
    }
    return read;
}

It is still not a hundred % correct but at leas i get more reliable results then before.

user207421
  • 305,947
  • 44
  • 307
  • 483
Bas Goossen
  • 459
  • 1
  • 7
  • 20
  • Can you show the code? IMO Java should raise exceptions when an error occurs. – Devolus Jun 16 '13 at 06:44
  • I agree with that, and i hope it whould. Normally it is supposed to fire an IOException which is caught and handled upon throw. In my case there are no exceptions, just the read that closes normally with a -1 result indicating EndOfStream according to the specification. – Bas Goossen Jun 16 '13 at 06:46
  • That's not true. `read() returns -1 at EOS. `readLine()` returns null at EOS. `readXXX()` throws `EOFException` at EOS for any other value of XXX. – user207421 Aug 14 '13 at 10:07
  • Yep but since it is not at EOS and reads on afterwards, an IOException should be thrown on the read error instead of returning a -1. Again this only happens on SSLSockets in java. The messages received are generated with Google Chrome's websocket implementation. – Bas Goossen Aug 15 '13 at 10:31

1 Answers1

0

i get an unexpected -1 result from socket.in.read(buffer,off,len)

You have already reached EOS (end of stream) before you called this method.

this happens also in the middle of a frame.

There is no such thing as a 'frame' in TCP. If you mean it happens in the middle of an application message, you have an application protocol error.

Normally i close a socket directly upon reception of -1, since it is end of stream.

Correct.

However i noted that it can also happen on a connection reset

No it doesn't. If it did, you could not possibly have detected the reset. The statement is self-contradictory.

I have come over many cases where in my tests the socket whould return valuable data after read returned -1.

No you haven't. A socket can't return anything but -1 after it first does so. You can't be getting any data at all, let alone 'valuable' data, unless you are ignoring the -1 somewhere.

My problem arrises when sometimes i just get some scrambled data out of the socket after such a case.

Only if you ignore the -1, as you are doing.

Another problem is that the other side is not notified when a frame cannot be delivered.

Of course it isn't. If you could deliver a notification to the other side, you could deliver the packet. This doesn't make sense either. If you mean that the other side doesn't get notified when it couldn't deliver the packet, you are up against the fact that TCP sends are asyncrhonous, so you won't normally get a send error on the send that caused it. You will get it on a later send. If you need per-send acknowledgements, you need to build them into your application protocol.

So what good is TCP/SSL then?

TCP is a reliable data-stream protocol, and SSL is a secure reliable data-stream protocol. That's what use they are.

if you need to consider it an unreliable connection for transporting websocket frames in java?

Neither of them is unreliable.

I hope that somebody knows what to do after read returns -1.

Close the socket.

Meanwhilst i have done some more testing and found that 9 out of 10 times it works if you continue reading after reception of -1.

No it doesn't. 1000 times of 1000 it continues to return -1. All you are seeing here is the effect of other bugs in your code.

So even if you are reading halfway the frame and receive a -1 then you should test somehow if the socket is closed or not

You can't. The socket isn't closed. Proof: you just read from it without getting an exception. You can't test whether the connection is closed either, other than by read() returning -1.

I now use: socket.isInputShutdown().

Pointless. That tells you whether you have called Socket.shutdownInput() on your own socket. It doesn't tell you diddly-squat about the state of the connection. There is no TCP API that can do that, other than reading or writing.

if this is not the case then just continue filling up the buffer.

I.e. reading gargabe by ignoring the -1 that read() is returning.

To do so i now use the following code where socket is the SSLSocket:

Why? DataInputStream.readFully() already exists. Re-implementing it won't help.

if(b < 0)
{
    Logger.log(TAG, "readFully read returned: " + b + " testing if connection is reset or closed.", Logger.WARNING);
    if(socket.isInputShutdown())

At this point it is 100% irrelevant whether your Socket is shutdown for input. read() has returned -1, which means the peer has closed the connection. Period.

    {
        throw new IOException("InputStream closed before data could be fully read! (readFully read returned -1 and socket.isInputShutdown() is true");
    }

This is all nonsense.

}
else
{
    buffer[off + (read++)] = (byte) b;
}

Here you are adding the low byte of -1, which is 0xff, to the buffer. This is also nonsense.

user207421
  • 305,947
  • 44
  • 307
  • 483
  • I'd love to agree with your answer as this is the way that it should work. It does with normal TCP-Sockets, however it seems that the SSL-Socket in Java contains a bug and that it does read a -1 whilst not at the end of the stream without the stream beïng closed. This is tested in a single threaded application with only one socket active and a single read loop. – Bas Goossen Aug 15 '13 at 10:26
  • Your test is invalid for the reasons I have stated above. You are attributing to `Socket.isInputShutdown()` magical properties that it simply does not have. The 'extra data' you claim you receive is nothing but an endless stream of 0xFF bytes caused by ignoring the -1 and thinking you know better than TCP and SSL when the peer has disconnected, when you don't. There is no such bug. Otherwise nothing would work, including for example this forum. – user207421 Aug 18 '13 at 10:27
  • I have received complete websocket frames on a stream that already indicated EOS. Also the webbrowser Chrome in the case keeps the websocket open at that moment. The Socket.isInputShutdown() has never returned anything usefull so there you are right, but i added it just in case when i was looking for a solution. So it is not an enless stream of 0xFF's. Your statement claiming nothing whould work is not valid, since this bug does not occur every minute. My clients are connected 24/7 and the sockets are opened 24/7. An invalid EOS is received a few times every hour on hundreds of connections. – Bas Goossen Aug 18 '13 at 20:01
  • I[m sorry but I simply cannot believe any of this. I suggest you try `DataInputStream.readFully().` – user207421 Aug 19 '13 at 07:46
  • I'm now switching to NIO since the problem does not occur there – Bas Goossen Sep 04 '13 at 13:23
  • The problem doesn't occur anywhere other than in your own code. – user207421 Nov 19 '13 at 00:32