Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Languages / C#

Easy and Perfomant Client/Server Communication with Protobuf-net & Griffin.Framework

0.00/5 (No votes)
3 Jun 2014LGPL32 min read 11.4K  
Easy and perfomant client/server communication with protobuf-net & Griffin.Framework

ProtoBuf is Google's 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 has 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 were running on the same machine.

The Message

The message being transferred and its reply looked like this:

C#
[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.

C#
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:

C#
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:

C#
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.

C#
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

griffinframework - Click to enlarge image

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.

License

This article, along with any associated source code and files, is licensed under The GNU Lesser General Public License (LGPLv3)