/// <summary> /// 发送json格式的Http Rpc远程调用 /// </summary> /// <typeparam name="T">结果对象的类型</typeparam> /// <returns>返回远程调用结果</returns> public async Task <T> Call <T>() { // 远程请求 byte[] data = null; using (var request = CreateRequestMessage()) using (var content = new PushStreamContent((ws) => RpcClientKit.WriteFrame(ws, _data, _isCompressed))) { request.Content = content; HttpResponseMessage response; try { response = await _client.SendAsync(request).ConfigureAwait(false); } catch (Exception ex) { throw new ServerException("服务器连接失败", $"调用【{_methodName}】时服务器连接失败!\r\n{ex.Message}"); } if (response.StatusCode != System.Net.HttpStatusCode.OK) { #if !SERVER // 无权限时 if (response.StatusCode == System.Net.HttpStatusCode.Unauthorized) { // 已登录则提示无权限 if (Kit.IsLogon) { throw new KnownException($"⚡对【{_methodName}】无访问权限!"); } // 跳转到登录页面 Kit.Login(true); throw new KnownException("请先登录您的账号!"); } #endif throw new ServerException($"服务器返回状态码:{response.StatusCode}", $"调用【{_methodName}】时返回状态码:{response.StatusCode}"); } var stream = await response.Content.ReadAsStreamAsync(); data = await RpcClientKit.ReadFrame(stream); response.Dispose(); } return(ParseResult <T>(data)); }
/// <summary> /// 读取从服务器返回的下一帧数据 /// </summary> /// <returns></returns> public async Task <bool> MoveNext() { try { // _responseStream.ReadAsync 使用 CancellationToken 也只有第一次取消时有效,所以未使用! // 此处只在服务端取消连接时抛出异常! var data = await RpcClientKit.ReadFrame(_responseStream); _val = RpcKit.ParseBytes <object>(data); return(true); } catch { Dispose(); } return(false); }
/// <summary> /// 向服务端写入一帧 /// </summary> /// <param name="p_message">支持序列化的对象</param> /// <returns></returns> public async Task <bool> Write(object p_message) { // 请求流已关闭 if (_rpc.RequestStream == null || _rpc.RequestCompleted) { _rpc.FinishRequest(); return(false); } try { await RpcClientKit.WriteFrame(_rpc.RequestStream, p_message); return(true); } catch { } _rpc.FinishRequest(); return(false); }
protected RequestWriter CreateWriter(HttpRequestMessage p_request) { _writeStreamTcs = new TaskCompletionSource <Stream>(TaskCreationOptions.RunContinuationsAsynchronously); _writeCompleteTcs = new TaskCompletionSource <bool>(TaskCreationOptions.RunContinuationsAsynchronously); p_request.Content = new PushStreamContent(async(stream) => { // 不同平台stream类型不同,造成uwp、android、ios客户端调用stream.FlushAsync()时并未发送! // asp.net core:System.Net.Http.Http2Connection.Http2Stream.Http2WriteStream // uwp:System.Net.Http.HttpContent.LimitMemoryStream // mono:System.Net.Http.HttpContent.FixedMemoryStream // 先发送调用帧 await RpcClientKit.WriteFrame(stream, _data, _isCompressed).ConfigureAwait(false); // 允许自定义写入 _writeStreamTcs.TrySetResult(stream); // 控制发送任务不结束,未结束前一直可发送 await _writeCompleteTcs.Task; }); return(new RequestWriter(this)); }
/// <summary> /// 启动Http2协议的远程调用,客户端发送一个请求,服务端返回数据流响应 /// </summary> /// <returns></returns> public async Task <ResponseReader> Call() { try { using (var request = CreateRequestMessage()) using (var content = new PushStreamContent((ws) => RpcClientKit.WriteFrame(ws, _data, _isCompressed))) { // 必须设置 WebAssemblyEnableStreamingResponse 为 true,WASM 才会返回 stream 类型的 response; // 如果不设置,则只在整个response结束时收到一次,无法实现服务器推送流模式!!!参见: // https://github.com/grpc/grpc-dotnet/blob/master/src/Grpc.Net.Client.Web/GrpcWebHandler.cs#L137 // // 此限制和服务端无关,只是WASM客户端在收到服务器推送后的处理方式不同,默认response 非 stream模式,参见: // https://github.com/mono/mono/blob/a0d69a4e876834412ba676f544d447ec331e7c01/sdks/wasm/framework/src/System.Net.Http.WebAssemblyHttpHandler/WebAssemblyHttpHandler.cs#L149 // // https://github.com/mono/mono/issues/18718 #if WASM #pragma warning disable CS0618 request.Properties["WebAssemblyEnableStreamingResponse"] = true; #pragma warning restore CS0618 #endif request.Content = content; // 一定是ResponseHeadersRead,否则只在结束时收到一次!!!众里寻他千百度 var response = await _client.SendAsync(request, HttpCompletionOption.ResponseHeadersRead, CancellationToken.None); response.EnsureSuccessStatusCode(); var reader = new ResponseReader(response); // wasm中增加上述设置后返回 stream 正常! await reader.InitStream(); return(reader); } } catch (Exception ex) { throw new Exception($"调用【{_methodName}】时服务器连接失败!\r\n{ex.Message}"); } }