You need to add an additional buffer component between your consumer DFA and the socket client.
Whenever data is avaliable from the NetworkStream
the buffer component will read it and append it to its own private buffer, incrementing an "available bytes" counter. The buffer component needs to expose at least the following functionality to its users:
- a
BytesAvailable
property -- this returns the value of the counter
- a
PeekBytes(int count)
method -- this returns the first count
bytes of the buffer, if that much is available at least, and does not modify the counter or the buffer
- a
ReadBytes(int count)
method -- as above, but it decrements the counter by count
and removes the bytes read from the buffer so that subsequent PeekBytes
calls will never read them again
Keep in mind that you don't need to be able to service an arbitrarily high count
parameter; it is enough if you can service a count
as long as the longest message it would be possible to receive at all times.
Obviously the buffer component needs to keep some kind of data structure that allows "wrapping around" of some kind; you might want to look into a circular (ring) buffer implementation, or you can just use two fixed buffers of size N
where N
is the length of the longest message and switch from one to the other as they become full. You should be careful so that you stop pulling in data from the NetworkStream
if your buffers become full and only continue pulling after the DFA has called ReadBytes
to free up some buffer space.
Whenever your DFA needs to read data, it will first ask your buffer stage how much data it has accumulated and then proceed accordingly. It would look something like this:
if BytesAvailable < 2
return; // no header to read, cannot do anything
// peek at the header -- do not remove it from the buffer!
header = PeekBytes(2);
// calculate the full message length based on the header
// if this is not possible from just the header, you might want to do this
// iteratively, or you might want to change the header so that it is possible
length = X;
if BytesAvailable < X
return; // no full message to read, cannot continue
header = ReadBytes(2); // to remove it from the buffer
message = ReadBytes(X); // remove the whole message as well
This way your DFA will only ever deal with whole messages.