views:

74

answers:

3

Hi,

I'm writing a protocol on top of sockets, so I've decided to implement headers then send the information. So there is one thread per connection on the server which sits there reading in headers, then delegates off to methods to read in the rest of the information when it arrives.

So essentially it looks like this:

while ((length = inStream.read(buffer)) != -1)
{
   dispatch(buffer, length);
}

So the dispatch method then decrypts the headers and delegates the method depending what is found in the header. It looks similar to:

byte[] clearText = decrypt(message,length);
if (cleartext == foo) sendFooToSocket();

So then sendFooToSocket() would then sit there and read from the instream or send to the outstream. This is where I seem to run into some problems, in the client I'm sending the header then flushing, then sending the rest of the data, but it appears it's all coming as one and not being split up into header then data. Also is there a best way to force out of the sendFooToSocket method?

public void sendFooToSocket()
{
  byte[] buffer = new byte[1024];
  int length = 0;
  while ((length = inStream.read(buffer) >0)
  {
    message = decrypt(buffer, length);
  }
}

I would assume flush would allow me to break out of this method as it closes then opens the stream?

So I have 2 problems, flush doesn't seem to be breaking up my messages and flush doesn't seem to be allowing to drop out of methods such as sendFooToSocket(), any suggestions?

For clarity sake, the client just does this:

byte[] header = "MESG".getBytes();
cipher = encrypt(header);
outStream.write(cipher,0,cipher.length);
outStream.flush();
byte[] message = "Hi server".getBytes();
cipher = encrypt(message);
outStream.write(cipher,0,cipher.length);
outStream.flush();

But this is received by the server as 1 message even though it's been flushed after every write. Sending just the header works, and we get stuck in the sendFooToSocket() method, but if I send the data after the flush it comes all at once.

The client uses OutputStream and InputStreams just from the socket.get. The client also uses OutputStream and InputStream. Not sure if this matters?

+3  A: 

What you seem to want is "record boundaries". With streams in general there are no implicit record boundaries. If you want that kind of functionality you will need to implement it yourself, by buffering the input and looking for, say, newlines to indicate the end of a record.

Look at BufferedInputStream.

Jim Garrison
So it gets buffered even though flush is called? So you can't rely on flush? Or you can't rely on the receiving end to not buffer data?
dekz
The flush() will force all data pending to be sent out. On the other end the read() should unblock. However you can't control for sure what all's in the pipe at the time read() returns. So you still need to manage record boundaries.
seand
All flush() does is guarantee that the data is passed on to the lower-level driver, whether that's the filesystem (in which case the data is supposed to be committed to disk) or the network (in which case the data is queued for transmission). You have no control over how the transmission ultimately occurs.
Jim Garrison
How do you overcome false positives when using a 'record boundary'. Using utf-8 and encryption, characters such as '\n' are used. I don't think having an array of characters such as 'STOP' for an example, would be a solution, just a work around?
dekz
You're designing the protocol. If you want record boundaries and encryption at the same time you must have two protocol sub-levels. The outer one works on byte streams (i.e. raw bytes) and provides a way to define record boundaries. The inner one can carry a byte stream consisting of encrypted UTF-8. There cannot be any ambiguity or 'false positives' if you structure your protocol correctly.
Jim Garrison
+1  A: 

inStream.read() may not be returning on a message boundary. You can't assume that it'll return at any particular boundary (such as a blank line separating headers and content if that's how you're doing it.) You'll have to manually parse the content and ignore the fact that it could come from multiple read()s or maybe one read() contains both the headers and content.

seand
So I can easily rewrite a read() method which reads until a new line is encountered, then just pass the stream onto the correct method. Is there a more elegant solution than to send a message, than send a new line? Or should I just wrap the message communication so it doesn't look so ugly.
dekz
+1  A: 

Unless you actually want control at the level you have implemented, you could consider Object streams (see ObjectInputStream and ObjectOutputStream). Such streams will allow you to send Java Objects over sockets and read them at the other end with out having to deal with headers and boundaries etc. See ObjectOutputStream for more details, but it's pretty much:

Sender: writeObject(objectX)

Receiver: myCopyOfObjectx = readObject()

and you can send any objects you like (as long as they are Serializable).

jowierun
Serialized objects can have gotchas. You're limited to Java (or whatever platform you're using). The data is opaque. It's useful if a comm protocol is simple enough so you can plug telnet/ncat into a listener for testing.
seand