Search code examples
c#.netprotobuf-netdata-transfer-objects

C# Best way to transfer Serialized Protobuff data thru http


I am using Marc Gravell's Protobub-Net to serialize and deserialize objects.

I am looking for the most efficient way to transfer data through an http:// request.

Here what I have done so far:

public string ProtobuffToString()
{
    Dictionary<int, decimal> List1 = new Dictionary<int, decimal>();
    List1.Add(2018, 1.2m);
    List1.Add(2017, 1.4m);
    List1.Add(2016, 1.9m);

    using (var stream = new MemoryStream())
    {
         ProtoBuf.Serializer.Serialize(stream, List1);
        return Convert.ToBase64String(stream.ToArray());
    }
}

public async System.Threading.Tasks.Task<string> ReadProtobuffAsync()
{
    using (System.Net.Http.HttpClient client = new System.Net.Http.HttpClient())
    using (System.Net.Http.HttpResponseMessage response = await client.GetAsync("http://localhost:53204/ProtobuffToString")) //<= Will call ProtobuffToString() above
    using (System.Net.Http.HttpContent content = response.Content)
    {
        string result = await content.ReadAsStringAsync();
        byte[] resultByte = Convert.FromBase64String(result);

        using (var stream = new MemoryStream(resultByte))
        {
            return JsonConvert.SerializeObject(ProtoBuf.Serializer.Deserialize<Dictionary<int, decimal>>(stream));
        }
    }
}

Can I do it better?

And is it better/faster than to transfer it from json/Json.Net?

Maybe yes because the data transfer will be smaller and the serialization/deserialization faster.


Solution

  • Can i do it better?

    Protobuf is a binary serialization format that is optimized for (de-)serialization and a low-footprint transfer. By converting the data to Base64 you add another conversion layer and increase the footprint of the transmission.

    And is it better/faster than to transfer it from json/Json.Net?

    There is no reasonable argument that would support that claim. Reiterating my previous statement, Protobuf is highly optimized, whereas JSON is some sort of trade-off that is not very efficient to serialize, nor very well readable.

    Having said that, have a small example, how to send your Protobuf via HTTP

    HttpClient client = new HttpClient();
    using (var stream = new MemoryStream())
    {
        // serialize to stream
        ProtoBuf.Serializer.Serialize(stream, List1);
        stream.Seek(0, SeekOrigin.Begin);
    
        // send data via HTTP
        StreamContent streamContent = new StreamContent(stream);
        streamContent.Headers.Add("Content-Type", "application/octet-stream");
        var response = client.PostAsync("http://what.ever/api/upload", streamContent);
    }
    

    If you'd like to receive Protobuf, you could modify your snippet like

    using (System.Net.Http.HttpClient client = new System.Net.Http.HttpClient())
    using (System.Net.Http.HttpResponseMessage response = await client.GetAsync("http://localhost:53204/ProtobuffToString")) //<= Will call ProtobuffToString() above
    using (System.Net.Http.HttpContent content = response.Content)
    using (var memoryStream = new MemoryStream())
    {
        await content.CopyToAsync(memoryStream);
        memoryStream.Seek(0, SeekOrigin.Begin); // reset stream
    
        return ProtoBuf.Serializer.Deserialize<Dictionary<int, decimal>>(memoryStream); // assuming that you return the Dictionary, not a string
    }
    

    Actually I don't see why you wanted to return the string instead of the Dictionary<int, decimal> from ReadProtobuffAsync. I'd recomment to drop the Dictionary altogether and use meaningful protobuf definitions and corresponding classes, because this is the really strong point of Protobuf.