.NET Zone is brought to you in partnership with:

.NET developer/architect. Been coding since 1996. Jonas is a DZone MVB and is not an employee of DZone and has posted 19 posts at DZone. You can read more from them at their website. View Full User Profile

Easy and Performant Client/Server Communication with protobuf-net & Griffin.Framework

06.03.2014
| 1865 views |
  • submit to reddit

ProtoBuf is Googles open serialization format which can be used to serialize objects in a standardized way. With it, different platforms can communicate with a format that is much more efficient than XML. Combine the most popular implementation if it, protobuf-net, with Griffin.Framework and you get an easy and fast way of sending information between processes.

(this is a follow up on my previous post)

As I know that the format is quite efficient I felt that it would be a powerful combination. I’ve therefore created a simple client/server where I made a benchmark for how long it would take for a Griffin.Framework client to send 20 000 messages to a Griffin.Framework server. The requirement was that the client have to receive the reply before sending the next message. To spice everything up I changed so that the client application used 20 clients simultaneously, and all clients had to receive all replies before the time was stopped.

As MicroMsg sends the type over the network it works well together with protobuf-net (which requires a specific type to be able to deserialize the message),

Both the server and client was running on the same machine.

The message

The message being transferred and it’s reply looked like this:

[ProtoContract]
public class Authenticate
{
    [ProtoMember(1)]
    public string UserName { get; set; }

    [ProtoMember(2)]
    public string Password { get; set; }
}

[ProtoContract]
public class AuthenticateReply
{
    [ProtoMember(1)]
    public bool Success { get; set; }

    [ProtoMember(2)]
    public string Decision { get; set; }
}

The serializer

To start with, I had to write a serializer for the library which uses protobuf.

class ProtoBufSerializer : IMessageSerializer
{
    static ConcurrentDictionary<string, Type> _types = new ConcurrentDictionary<string, Type>();

    public void Serialize(object source, Stream destination, out string contentType)
    {
        Serializer.NonGeneric.Serialize( destination, source);
        contentType = "application/protobuf;" + source.GetType().FullName;
    }

    public object Deserialize(string contentType, Stream source)
    {
        Type type;
        if (!_types.TryGetValue(contentType, out type))
        {
            int pos = contentType.IndexOf(";");
            if (pos == -1)
                throw new NotSupportedException("Expected protobuf");

            type = Type.GetType(contentType.Substring(pos + 1), true);
            _types[contentType] = type;
        }
            
        return Serializer.NonGeneric.Deserialize(type, source);
    }
}

As you can see, I also use a Type cache as Type.GetType() is terrible slow, even thought it has a built in cache. You could implement a JSON or Xml serializer in the same way.

The server

As a server you typically just create a ChannelTcpListener:

var settings = new ChannelTcpListenerConfiguration(
    () => new MicroMessageDecoder(new ProtoBufSerializer()),
    () => new MicroMessageEncoder(new ProtoBufSerializer())
    );

var server = new MicroMessageTcpListener(settings);
server.MessageReceived = OnServerMessage;

server.Start(IPAddress.Any, 1234); 

That’s it, all you need now is a callback to receive messages from all clients:

private static void OnServerMessage(ITcpChannel channel, object message)
{
    var auth = (Authenticate) message;
    channel.Send(new AuthenticateReply() {Success = true});
}

How much code/configuration would you need in WCF to achieve the same thing?

The client

In the client you do about the same, but you use async/await instead of a callback.

private static async Task RunClient()
{
    var client = new ChannelTcpClient<object>(
        new MicroMessageEncoder(new ProtoBufSerializer()),
        new MicroMessageDecoder(new ProtoBufSerializer())
        );

    await client.ConnectAsync(IPAddress.Parse("192.168.1.3"), 1234);

    for (int i = 0; i < 20000; i++)
    {
        await client.SendAsync(new Authenticate { UserName = "jonas", Password = "king123" });
        var reply = (AuthenticateReply)await client.ReceiveAsync();

        if (reply.Success)
        {
            //Console.WriteLine("Client: Yay, we are logged in.");
        }
        else
        {
            //Console.WriteLine("Client: " + reply.Decision);
        }
    }

    await client.CloseAsync();
}

The result

If you study the screenshot, you will see that Griffin.Framework only takes 28% of the total time for those messages. 37% of the time is used by protobuf-net. The remaining 35% is spent in .NET/Windows.


On my machine that means that 800 000 .NET objects (400 000 requests and 400 000 replies) where transferred in 11 seconds time. That’s 0,01398375 milliseconds per object. Those digits are not as important as the fact that Griffin.Framework only took 28% of the time being used by the application.

Published at DZone with permission of Jonas Gauffin, author and DZone MVB. (source)

(Note: Opinions expressed in this article and its replies are the opinions of their respective authors and not those of DZone, Inc.)