You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Dart编写的gRPC客户端流结束后服务端阻塞问题求助

问题:gRPC流式上传图片时服务端无法退出await for循环

问题背景

我希望通过gRPC将图片从客户端上传至服务端,编写了相关proto文件、客户端和服务端代码,图片已成功发送至服务端(可通过打印确认),但服务端始终无法退出await for循环,客户端一段时间后触发超时错误。

相关代码

Proto文件

syntax = "proto3";

service EshopService {
    rpc UploadImage (stream ImageToUpload) returns (ImageLink);
}

message ImageToUpload {
    bytes image = 1;
}
message ImageLink {
    string image_links = 1;
}

客户端代码

Future<ImageLink> uploadImage(ImageToUpload image) async {
    return await stub.uploadImage(streamImage(images.image));
}

Stream<ImageToUpload> streamImage(List<int> image) async* {
    for (var element in image) {
        yield ImageToUpload(image: [element]);
    }
}

服务端代码

Future<ImageLink> uploadImage(grpc.ServiceCall call, Stream<ImageToUpload> request) async {
    print('uploading');
    final List<int> image = [];
    await for (var bytes in request) {
        image.addAll(bytes.image);
    }
    print('image gotten');
    File imageFile = File('image.jpg');
    await imageFile.writeAsBytes(image);
    return ImageLinks(imageLinks: 'image');
}

错误信息

gRPC Error (code: 4, codeName: DEADLINE_EXCEEDED, message: Deadline exceeded, details: null, rawResponse: null, trailers: {})

问题原因与解决方法

核心问题

  1. 服务端存在类型错误:返回的ImageLinks与proto定义的ImageLink不匹配,导致服务端无法正确生成响应,客户端一直等待最终超时。
  2. 客户端流发送逻辑不合理:逐个字节拆分发送消息,不仅效率极低,还可能导致gRPC无法正确识别流结束信号。

修复步骤

  1. 修正服务端返回类型
    将服务端的返回语句改为与proto定义一致的类型:

    return ImageLink(imageLinks: 'image');
    
  2. 优化客户端流发送逻辑
    改为按固定大小分块发送图片字节,既提升效率,又能让gRPC正确识别流结束:

    Stream<ImageToUpload> streamImage(List<int> image) async* {
        const chunkSize = 1024; // 按1KB分块
        for (int i = 0; i < image.length; i += chunkSize) {
            int end = i + chunkSize;
            if (end > image.length) end = image.length;
            yield ImageToUpload(image: image.sublist(i, end));
        }
        // async*函数执行完毕后,流会自动关闭并发送结束信号
    }
    
  3. 设置合理的超时时间
    客户端调用时可手动设置合适的超时,避免因网络或处理耗时触发不必要的超时:

    Future<ImageLink> uploadImage(List<int> imageBytes) async {
        final callOptions = CallOptions(timeout: Duration(seconds: 30));
        return await stub.uploadImage(streamImage(imageBytes), options: callOptions);
    }
    

验证逻辑

修复后,客户端流发送完毕时,gRPC会自动向服务端发送流结束信号,服务端的await for循环会正常退出,完成图片写入后返回正确的响应,客户端不会再触发超时错误。

内容的提问来源于stack exchange,提问作者Kimiya Zargari

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.30 03:53:12