ASP.NET Core 1.1分块响应

7
希望有人能为我解答以下问题。
我有一个客户端,将向控制器端点发送请求(没有视图,从c#到c#,甚至是C++)。控制器将必须异步获取响应并以json格式发送它们(向客户端发送json1,然后是json2,json3等,直到关闭连接或发送类似的空终止文本)。目的是将结果流式传输回客户端,以便在服务器仍在工作时可以开始处理。
我的控制器端点如下所示:
        [HttpGet("testStream")]
        public async Task testStream()
        {
            var response = HttpContext.Response;
            response.Headers[HeaderNames.TransferEncoding] = "chunked";


            for (var i = 0; i < 10; ++i)
            {
                await response.WriteAsync($"6\r\ntest {i}\r\n");

                await response.Body.FlushAsync();
                await Task.Delay(1 * 1000);
            }
            await response.WriteAsync("0\r\n\r\n");
            await response.Body.FlushAsync();
        }

我的测试看起来像这样:

        static async void DownloadPageAsync()
        {
            // ... Target page.
            string page = "http://localhost:8080/api/Stream/testStream";
            Console.WriteLine("test");
            while (!Debugger.IsAttached) Thread.Sleep(500);
            // ... Use HttpClient.
            using (HttpClient client = new HttpClient())
            using (var response = await client.GetAsync(page, HttpCompletionOption.ResponseHeadersRead))
            using (HttpContent content = response.Content)
            {
                string result = await content.ReadAsStringAsync();
                do
                {
                    Console.WriteLine(result);
                    result = await content.ReadAsStringAsync();
                }
                while (result != "null");
            }
            Console.WriteLine("END");
        }
        [Fact]
        public void Test1()
        {
            TestSurvey.DownloadPageAsync();
        }

当我调用content.ReadAsStringAsync();时,我遇到了异常。

System.Net.Http.HttpRequestException : Error while copying content to a stream.
[xUnit.net 00:01:14.5836121]       ---- System.IO.IOException : The read operation failed, see inner exception.
[xUnit.net 00:01:14.5836496]       -------- System.Net.Http.CurlException : Failure when receiving data from the peer
[xUnit.net 00:01:14.5846837]       Stack Trace:
[xUnit.net 00:01:14.5857807]            at System.Net.Http.HttpContent.<LoadIntoBufferAsyncCore>d__48.MoveNext()

编辑:异常是因为未发送块的大小

await response.WriteAsync($"6\r\ntest {i}\r\n");

但是现在在测试/客户端方面,我一次性收到了所有块...


为什么你要让你的代码变得不够高效呢?比如 await Task.Delay(1 * 1000); - Liam
1
基本上,不要这样做。只需将数据发送回去即可。如果需要,HTTP会进行分块传输。你在这里做的只是减慢了服务速度,而不是加快它。 - Liam
这是为了测试目的,我想看到答案慢慢返回... - Jon
这就是将要发生的事情。传输是分块的。它以HTTP分块发送。客户端会等待所有块到达后再继续。分块用于防止HTTP数据包过大,它不是异步通信方法。你在这里想要实现什么? - Liam
那不可能发生。如果你想要那个,你需要自己拆分响应。异步仅在等待外部资源时停留主线程,而块是一种通信协议实现。这两件事都不能做你想要的事情。基本上,我很抱歉,你错了 :) - Liam
显示剩余6条评论
1个回答

6
为了解决这个问题,我使用了SSE或服务器端事件。以下是asp.net core中的服务器端代码:
        [HttpGet("testStream")]
        public async Task testStream()
        {
            var response = HttpContext.Response;
            response.StatusCode = 200;
            response.ContentType = "text/event-stream";

            for (var i = 0; i < 10; ++i)
            {
                //the tags are either 'events:' or 'data:' and two \n indicates ends of the msg
                //event: xyz \n\n
                //data: xyz \n\n
                await response.WriteAsync($"data: test {i}\n\n");

                response.Body.Flush();
                await Task.Delay(5 * 1000);
            }
            await response.WriteAsync("data:\n\n");
            await response.Body.FlushAsync();
        }

以下是客户端代码:

        string page = "http://localhost:8080/api/Stream/testStream";

        //while (!Debugger.IsAttached) Thread.Sleep(500);

        using (HttpClient client = new HttpClient())
        using (var s = await client.GetStreamAsync(page))
        {
            using (StreamReader r = new StreamReader(s))
            {
                string line = null;
                while (null != (line = r.ReadLine()))
                {
                    Console.WriteLine(line);
                }
            }
        }

使用ReadAsStringAsync会强制等待所有消息才能继续进行。


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