如何从响应流中读取数据 HttpWebRequest C#

4

我正在开发一个Xamarin应用程序。我还处于非常基础的水平阶段,之前使用过Nativescript和一些Native Android。

我有一个执行长时间操作的Express服务器。在此期间,Xamarin客户端将等待并显示旋转图标。

在服务器上,我已经计算了作业的百分比进度,并希望每次更改时将其发送到客户端,以便将旋转图标替换为进度条。

然而,在服务器上,任务已经通过response.write('10');完成,其中数字10表示完成的"10%"。

现在是困难的部分。我该如何从流中读取这个10?目前它作为JSON响应工作,因为它等待整个响应过来。

Xamarin客户端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;
        }
    }
}

服务器会在作业进度更改时向响应中写入内容,发送包含百分比值的普通字符串。在作业结束时,它将写入一个最终字符串,该字符串将是一个 Base64(非常长的)字符串。然后响应将被关闭。
有人能指导我如何更改该脚本以便读取服务器发送的每个数据块吗?

你的服务器最终发送什么,是进度值还是JSON?或者是先发送进度值再发送JSON? - Evk
它可以发送任何内容,目前它正在发送一个包含当前作业进度的普通字符串。最后,它将在JSON结构中发送一个Base64字符串,如下所示:{data: "base64string-value"} - Caius
如果服务器需要更改以便简化过程,那么可以进行更改。 - Caius
1个回答

2

首先,您需要定义一些协议。为了简单起见,我们可以说服务器发送:

  • (可选)当前进度作为三位数的字符串(“010”表示10%)
  • (必需)最终进度为“100”
  • (必需)json数据

因此,有效的响应是例如“010020050090100 {..json here..}”。

然后,您可以以3字节块读取响应,直到找到“100”。然后读取json。示例代码:

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;
}

哥们,谢谢!它完美地运行了。只有两个快速问题。使用阻塞 while(true),旋转器停止旋转。它似乎被冻结了。第二个问题是,为什么建议不使用 Task.Run?我只是好奇,因为我还是新手,想学到更多。 - Caius
@Caius 好的,阻塞式读取是错误的,已更改为 await ReadAsync。至于 Task.Run - 如果 JsonValue 没有对应的异步方法(比如 JsonValue.LoadAsync),并且它会阻塞用户界面线程 - 那么使用 Task.Run 是可以接受的。 - Evk
@Caius 为了澄清,阻塞的不是 while (true),而是 stream.Read 调用,你需要将其更改为 await stream.ReadAsync - Evk
谢谢,谢谢,谢谢!感谢您花费时间并清晰地解释。现在一切都运行顺畅。 - Caius

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接