4

I'm building a Xamarin app. I'm still on a very very noobish level, and I'm coming from Nativescript, and something (not much) of Native Android.

I have an Express server that performs long-time operations. During that time the Xamarin client waits with a spinner.

On the server I already calculate the percentage progress of the job, and I'd like to send it to the client each time it changes, in order to swap that spinner with a progress.

Still, on the server, the task was already achieved with a response.write('10'); where the number 10 stands for "10%" of the Job done.

Now the tuff part. How can I read that 10 from the stream? Right now it works as a JSON response, because it waits for the whole response to come.

Xamarin client HTTP GET:

// Gets weather data from the passed URL.
async Task<JsonValue> DownloadSong(string url)
    {
        // Create an HTTP web request using the URL:
        HttpWebRequest request = (HttpWebRequest)WebRequest.Create(new Uri(url));
        request.ContentType = "application/json";
        request.Method = "GET";

    // Send the request to the server and wait for the response:
    using (WebResponse response = await request.GetResponseAsync())
    {
        // Get a stream representation of the HTTP web response:
        using (System.IO.Stream stream = response.GetResponseStream())
        {
            // Use this stream to build a JSON document object:
            JsonValue jsonDoc = await Task.Run(() => JsonValue.Load(stream));

            // Return the JSON document:
            return jsonDoc;
        }
    }
}

The server writes on the response each time the progress of the job changes, sending a plain string containing the percentage value. At the end of the job, it will write a final string, which will be a Base64 (very long) string. And the response will be then closed.

Can anyone indicate me how to change that script in order to read each data chunk the server sends?

3
  • What your server sends after all, progress values or json? Or progress values followed by json? Commented Apr 2, 2018 at 13:10
  • Well it could send anything, currently it's sending a plain string containing the current job progress as a string. And at the end of all it will send a Base64 string inside a JSON struct like this: {data: "base64string-value"} Commented Apr 2, 2018 at 14:24
  • If the server has to change in order to ease the process, it can. Commented Apr 2, 2018 at 14:51

1 Answer 1

2

First you need to define some protocol. For simplicity we can say that server sends:

  • (optional) current progress as 3-digit string ("010" - means 10%)
  • (required) final progress as "100"
  • (required) json data

So, valid response is, for example, "010020050090100{..json here..}".

Then you can read response in 3-byte chunks, until you find "100". Then you read json. Sample code:

using (System.IO.Stream stream = response.GetResponseStream()) {
    while (true) {
        // 3-byte buffer
        byte[] buffer = new byte[3];
        int offset = 0;
        // this block of code reliably reads 3 bytes from response stream
        while (offset < buffer.Length) {
            int read = await stream.ReadAsync(buffer, offset, buffer.Length - offset);
            if (read == 0)
                throw new System.IO.EndOfStreamException();
            offset += read;
        }
        // convert to text with UTF-8 (for example) encoding
        // need to use encoding in which server sends
        var progressText = Encoding.UTF8.GetString(buffer);
        // report progress somehow
        Console.WriteLine(progressText);
        if (progressText == "100") // done, json will follow
            break;
    }
    // if JsonValue has async api (like LoadAsync) - use that instead of
    // Task.Run. Otherwise, in UI application, Task.Run is fine
    JsonValue jsonDoc = await Task.Run(() => JsonValue.Load(stream));
    return jsonDOc;
}
Sign up to request clarification or add additional context in comments.

4 Comments

Man, thank you! It works like a charm. Just two quick questions. Using the blocking while(true), the spinner stops spinning. It appears freezed. second, why do you suggest to not use Task.Run? I'm just curious about this, since I'm still new, I'd like to learn the more i can.
@Caius yes sure, blocking Read is wrong, changed to await ReadAsync. As for Task.Run - if JsonValue doesn't have corresponding async method (something like JsonValue.LoadAsync) and it blocks your UI thread - then using Task.Run is fine.
@Caius to clarify, that's not while (true) which is blocking, but stream.Read call, which you need to change to await stream.ReadAsync.
Thank you, thank you thank you! For the time taken and the clear explanation. Now everything runs smoothly.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.