Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C# Sending multiple objects over same Socket

I'm trying to send multiple different objects (well, they are the same object types, just with different values) from one socket to another. I can send one object just fine using the following code:

Client:

var buffer = new byte[1024];
var binFormatter = new BinaryFormatter();
using (var ms = new MemoryStream())
{
    int bytesRead;
    while ((bytesRead = _clientReceive.Receive(buffer)) > 0);
    {
        ms.Write(buffer, 0, bytesRead);
    } 

    ms.Position = 0;
    message = (Message)binFormatter.Deserialize(ms);
} 

server:

var gm = new Message { Message = "ObjectType", Object = Data };

using (var mem = new MemoryStream())
{
    var binFormatter = new BinaryFormatter();
    binFormatter.Serialize(mem, gm);
    var gmData = mem.ToArray();
    client.Send(gmData, gmData.Length, SocketFlags.None);
    client.Close();
}

but if I want to send multiple messages (put the full client code in a loop and not call client.Close()) how would I be able to determine when the full object has been received by the client? Putting the client code in a loop like this:

while (_isReceivingStarted)
{
    var buffer = new byte[1024];
    var binFormatter = new BinaryFormatter();
    using (var ms = new MemoryStream())
    {
        int bytesRead;
        while ((bytesRead = _clientReceive.Receive(buffer)) > 0)
        {
            ms.Write(buffer, 0, bytesRead);
        }
        ms.Position = 0;
        message = (Message) binFormatter.Deserialize(ms);
    }
    // Do stuff then wait for new message
}

the client will hang at _clientReceive.Receive(buffer) because it doesn't receive the Close() from the client and never get the 0 received bytes. If I close the connection on the server, it will loop through and then error out at the Deserialize MemoryStream instead of blocking at the_clientReceive.Receive(buffer) in anticipation of another object being sent.

I hope this makes sense. Any pointers?

like image 502
joe_coolish Avatar asked Jun 01 '26 22:06

joe_coolish


2 Answers

I would highly recommend looking at Windows Communication Foundation. It will handle all of this plumbing for you, including serializing and deserializing your types across the wire, through multiple configurable channels, etc.

like image 143
Reed Copsey Avatar answered Jun 03 '26 11:06

Reed Copsey


When using a socket like this, I would use something like protobuf-net instead of BinaryFormatter (caveat/disclosure: I wrote it) In particular, if you write each item with Serializer.SerializeWithLenthPrefix(...) using Base128 for the prefix-style (one of the options), and some known tag (the other option) such as 1, then at the other end you can just use DeserializeItems<Foo>(...) (again specifying Base128 and 1).

This will correctly pick off items without trying to over-read until the stream is closed, when it will yield break. Each object is returned (yield return) separately, so it won't try to consume the entire stream before giving you data.

It can also be used with heterogeneous data if you like, but homogeneous is easiest.

like image 22
Marc Gravell Avatar answered Jun 03 '26 11:06

Marc Gravell