基于同一HTTP/2请求响应流实现全双工通信的问题排查
问题描述
我在Chrome 114客户端与ASP.NET Core 7服务器之间,尝试将单个HTTP/2请求-响应流作为全双工通信通道:客户端使用ReadableStream/WritableStream,服务器使用PipeReader/PipeWriter,目的是同时发送长请求、接收长响应。实际测试功能可用,但必须等请求完全发送完成后才能读取响应。
核心疑问:
- 为什么客户端代码中的读取操作无法与写入操作同步打印日志?
- 无法将fetch的
duplex属性设置为"full",浏览器不识别该值。浏览器中的HTTP/2不支持全双工吗?HTTP/3是否存在同样问题?
客户端代码(JavaScript)
let writeTotal = 0; let readTotal = 0; const reqStream = new ReadableStream({ pull: (controller) => { controller.enqueue("Yousuf is lovely!\n"); writeTotal += 18; // 为什么所有写入日志先打印,之后才轮到读取日志? console.log("Write: " + writeTotal); if (writeTotal === 18 * 100000) { controller.close(); } }, }); const resStream = new WritableStream({ write: (chunk) => { readTotal += chunk.length; // 为什么读取日志不能和写入日志同步打印? console.log("Read: " + readTotal); } }) const result = await fetch("https://localhost:8080/", { method: "POST", body: reqStream.pipeThrough(new TextEncoderStream()), duplex: 'half' // 没法设为full! }); await result.body.pipeThrough(new TextDecoderStream()).pipeTo(resStream); console.log("Total Written: " + writeTotal); console.log("Total Read: " + readTotal);
服务器端代码(C#)
using System.Buffers; using System.IO.Pipelines; using System.Text; var builder = WebApplication.CreateBuilder(args); var app = builder.Build(); app.Map("/", async (context) => { PipeReader reader = context.Request.BodyReader; PipeWriter writer = context.Response.BodyWriter; long total = 0; // 这里打印的是HTTP/2 Console.WriteLine("New Request: " + context.Request.Protocol); while (true) { ReadResult result = await reader.ReadAsync(); ReadOnlySequence<byte> buffer = result.Buffer; SequencePosition position = buffer.GetPosition(0); string body = Encoding.UTF8.GetString(buffer.Slice(position)); Console.WriteLine("New Chunk: " + body.Length); Console.WriteLine(body.Substring(0, Math.Min(10, body.Length))); Console.WriteLine("\n"); await writer.WriteAsync(Encoding.UTF8.GetBytes(body)); reader.AdvanceTo(buffer.GetPosition(buffer.Length)); total += body.Length; if (result.IsCompleted || result.IsCanceled || total == 18 * 100000) { Console.WriteLine("Completed? " + result.IsCompleted); Console.WriteLine("Canceled? " + result.IsCanceled); await reader.CompleteAsync(); await writer.CompleteAsync(); Console.WriteLine("\n"); Console.WriteLine("Total: " + total); Console.WriteLine("\n"); break; } } }); app.Run();
内容的提问来源于stack exchange,提问作者geeko
相关产品推荐
相关产品推荐

