John J. Hughes II wrote:
Using the below code I am send multiple sterilized object across an IP port.
This works fine if only one object is received at a time but with packing
sometimes there is more then one object or half an object in the received
data. If I place the data in a memory stream on the received side is there
a way to determine where one ends and the next one start?
Through _painstaking_ tracing and hours of debugging thinking that a
network card driver was buggy, I have found that:
- The SoapSerializer. Deserialize (sometimes) read more data than
SoapSerializer. Serialize generates (due to buffered reads).
- The BinarySerialize r.Deserialize doesn't always read all the data
generated by BinarySerialize r.Serialize, and will Close() the stream
when done deserializing!
So basically, you cannot send multiple serialized objects down the same
stream without being really lucky, especially after .NET-sp1.
SoapSerializer used to work most of the time before .NET-sp1.
My solution for sending multiple serialized objects, in a streaming way,
inside the same trasport-Stream is to implement a Stream that does
chunk-length en/de-coding of the amount of data written.
So I have a BlockSubStreamW riter, that writes a 16-bit length and then
the data to be written to the stream, and writes a 16-bit 0-length and
flushes to mark Close(), and a BlockSubStreamR eader that reads 16-bit
length and then corresponding data, making EOF if the length is 0, and
"eating" the rest of stream untill a 0-length is received if the
BlockSubStreamR eader is Close() or Dispose()'ed without having read a
0-length.
So now i can say
new BinarySerialize r.Serialize(new BlockSubStreamW riter(stream), graph);
new BinarySerialize r.Deserialize(n ew BlockSubStreamR eader(stream)).
On top of this i have a 4-byte magic ID, so I can do
serialize/deserialize using multiple protocols from a simple interface:
public interface IObjectChannel: IDisposable
{
/// <summary>
/// Send object down the channel
/// </summary>
void SendObject(Obje ct o);
/// <summary>
/// Read object from channel
/// </summary>
/// <exception cref="ObjectCha nnels.Closed">O bjectChannels.C losed
is thrown if the transport is closed
/// before any data is received</exception>
Object ReceiveObject() ;
}
I have a (rather pretty, if I should say it myself) implementation (and
some rather nifty handshaking and stuff) making up a transparent way of
shipping objects from one .NET instance to another, including extensive
unit and integration-tests.
Clients can now do (/actual code from test/)
using ( IObjectChannel c = ksio.ChannelPoo l.Global.Connec t(host,port))
{
c.SendObject(co mmand);
Object reply = c.ReceiveObject ();
}
Which will reuse an existing connection if one is available, and GC
connections that are idle for more than a specific time (default: 1minute).
Servers simply do (/actual code from test - implements an "echo-service"/)
Socket s = ...;
try
{
using ( NetworkStream stream = new NetworkStream(s , true) )
while ( true )
using ( IObjectChannel c =
ObjectChannels. Auto.Global.Cre ate(stream) )
c.SendObject(c. ReceiveObject() );
}
catch ( ObjectChannels. MagicIdObjectCh annel.Closed)
{
// That's the expected way to close
}
catch ( Exception e )
{
Console.WriteLi ne("UNEXPECTED END\n{0}", e);
}
The entire stuff (including a version that does gzip compression on the
data, thanks to ICSharpCode.Sha rpZipLib) is about 200kb worth of code,
..proj- and .sln-files, which I would be happy to publish if someone
wants to check it out.
--
Helge