gRPC双向流在C#中对应方法签名是public async Task BidirectionalStreaming(IAsyncEnumerable requestStream, IServerStreamWriter responseStream, ServerCallContext context);客户端必须用CallAsync()调用,且proto需声明rpc Chat(stream Message) returns (stream Message)。
gRPC 双向流在 C# 中对应什么方法签名
双向流(Bidi Streaming)在 C# gRPC 中必须使用 IAsyncEnumerable 作为参数,返回 IAsyncEnumerable。不能用普通 Task 或单次 ValueTask,否则编译通过但运行时会报错 Status(StatusCode=Unimplemented, Detail="Method not found")。
服务端方法签名必须是 public async Task BidirectionalStreaming(IAsyncEnumerable requestStream, IServerStreamWriter responseStream, ServerCallContext context) 或更常见的异步迭代写法(见下条):
- 客户端调用必须用
CallAsync()而非AsyncUnaryCall等其他方式 - proto 文件中该 RPC 必须声明为
rpc Chat(stream Message) returns (stream Message);—— 两个stream缺一不可 - 生成的客户端类里对应方法名后缀是
Async,且参数/返回类型严格匹配生成器输出(如ChatAsync)
客户端如何正确发送+接收并避免挂起或丢消息
常见错误是只 await foreach 接收,却没主动发请求;或用 Channel 发送但未调用 Writer.CompleteAsync() 导致服务端永远等不到流结束信号。
推荐结构:用 Channel 做发送缓冲,同时启动两个并发任务(发送 + 接收),并在退出前显式关闭发送通道:
var channel = Channel.CreateUnbounded();
using var call = client.ChatAsync();
_ = Task.Run(async () =>
{
await foreach (var req in channel.Reader.ReadAllAsync())
{
await call.RequestStream.WriteAsync(req);
}
await call.RequestStream.CompleteAsync(); // 关键:通知服务端“我不再发了”
});
// 启动接收
_ = Task.Run(async () =>
{
await foreach (var resp in call.ResponseStream.ReadAllAsync())
{
Console.WriteLine(resp.Content);
}
});
// 示例:发一条消息
await channel.Writer.WriteAsync(new Request { Content = "hello" });
注意:call.ResponseStream.ReadAllAsync() 是 .NET 6+ 才支持的扩展方法;若用 .NET 5,需手动 while (await call.ResponseStream.MoveNext())。