2
votes

I wrote a server that uses servicestack, and a client that connects to it using both JSON and protobuf-net (so I'm sure the server works...). Now I need to develop the same client on Windows Mobile with CF3.5, and since servicestack is not supported on CF3.5 I used on the client HttpWebRequest and NewtonSoft.Json.Compact for the json part, this way:

Classes:

[ProtoContract]
public class ReqPing 
{
}

[ProtoContract]
public class RespPing
{
    [ProtoMember(1)]
    public string Result { get; set; }
}

Function:

ReqPing iReqPing = new ReqPing();

string json = JsonConvert.SerializeObject(iReqPing);

HttpWebRequest req = (HttpWebRequest)WebRequest.Create("http://192.168.0.87:82/json/syncreply/ReqPing");
req.ContentType = "text/json";
req.Method = "POST";
req.ContentLength = json.Length;

            using (var streamWriter = new StreamWriter(req.GetRequestStream()))
            {
                streamWriter.Write(json);
                streamWriter.Flush();
                streamWriter.Close();
            }

            HttpWebResponse resp = (HttpWebResponse)req.GetResponse();

            Stream respStream = resp.GetResponseStream();

            string resps;

            using (var reader = new StreamReader(respStream, Encoding.UTF8))
            {
                resps = reader.ReadToEnd();                
            }

            respStream.Close();

            JsonTextReader jreader = new JsonTextReader(new StringReader(resps));
            JsonSerializer serializer = new JsonSerializer();
            RespPing iRespPing = serializer.Deserialize<RespPing>(jreader);

and it works, so now I'm trying to implement the same with protobuf, and I'm stuck here:

ReqPing iReqPing = new ReqPing();


var ms = new MemoryStream();?????? correct way?

Serializer.Serialize<ReqPing>(ms, iReqPing);

HttpWebRequest req = (HttpWebRequest)WebRequest.Create("http://192.168.0.87:82/x-protobuf/reply/ReqPing");
            req.ContentType = "application/x-protobuf";
            req.Method = "POST";
            req.ContentLength = ????????

how can I write the serialized stream to req??               

            HttpWebResponse resp = (HttpWebResponse)req.GetResponse();

            Stream respStream = resp.GetResponseStream();

            RespPing iRespPing = Serializer.Deserialize<RespPing>(respStream);

Anyone can suggest me what's the right way?

Thanks! Mattia

EDIT:

Ok on how to use the stream, but I need to set ContentLength too, or I get an error message on GetRequestStream (saying that I must set ContentLength...). I made it work with:

byte[] data;
        using(var stream = new MemoryStream()) 
        {
            Serializer.Serialize(stream, iReqPing);
            data = stream.ToArray();
            req.ContentLength = data.Length;
        }

        using(var stream = req.GetRequestStream()) 
        {
            stream.Write(data, 0, data.Length);                
        }

Is there another obvious way I'm missing?

Another question: I made it work but after two request, at the third it timeouts in GetRequestStream. I noticed I forgot in my previous call to make a call to Close() in my HttpWebResponse, I corrected it and now works. The strange thing is that I forgot to close HttpWebResponse even with my JSON version,and there I had no problems...the routine is exactly the same.. can you guess the reason? Thanks again!

1
I'm confused - you're talking about serialization but calling deserialize. Are you trying to read here? Or write? - Marc Gravell♦
iReqPing is my request, and I suppose that I must Serialize it to send it with HttpWebRequest. After that, I get the response stream and try to deserialize it in my iRespPing response object. I trying to follow the same schema I used for the previous JSON example, bu want to serialize with protobuf instead of JSON. - Mattia Durli

1 Answers

1
votes

how can I write the serialized stream to req??

using(var stream = req.GetRequestStream()) {
    Serializer.Serialize(stream, iReqPing);
}

and then to deserialize, as you already have correctly:

RespPing iRespPing;
using(var stream = resp.GetResponseStream()) {
    iRespPing = Serializer.Deserialize<RespPing>(respStream);
}

No need for a MemoryStream unless you specifically want to sent the length manually. If you do want to buffer, then maybe:

byte[] data;
using(var stream = new MemoryStream()) {
    Serializer.Serialize(stream, iReqPing);
    data = stream.ToArray();
}
...
using(var stream = req.GetRequestStream()) {
    stream.Write(data, 0, data.Length);
}

Edit to show more efficient use of MemoryStream re the now-edited question:

    byte[] data;
    int len;
    using(var stream = new MemoryStream()) 
    {
        Serializer.Serialize(stream, iReqPing);
        data = stream.GetBuffer(); // note this is oversized!
        len = (int)stream.Length;
    }
    req.ContentLength = len;

    using(var stream = req.GetRequestStream()) 
    {
        stream.Write(data, 0, len); 
    }

The subtlety here is that we have avoided having to duplicate the MemoryStream's buffer - instead we obtain the oversized back-buffer (remembering to note how much of it is actually data), and then write just that many bytes. So data.Length could be (as an example) 4096, but len could be 3012. This avoids an extra byte[] allocation and a Buffer.BlockCopy.