private static async void SendCallbackData(Task <MethodCallbackInfo> callback, ClientInfo client, ServerBase serverBase) #endif { try { #if (NET35 || NET40) MethodCallbackInfo callbackResult = callback.Result; #else MethodCallbackInfo callbackResult = await callback; #endif string json = ServerSerializationHelper.SerializeObject(callbackResult, serverBase); byte[] bytes = Encoding.UTF8.GetBytes(json); List <byte> result = new List <byte>(); result.Add((byte)DataType.ResponseCallMethod); result.Add((byte)CompressMode.None); result.AddRange(BitConverter.GetBytes(bytes.Length)); result.AddRange(bytes); await client.StreamHelper.WriteToStreamAsync(client.ClientStream, result.ToArray()); } catch (Exception ex) { serverBase.AutoLogger.LogError(ex, $"{client.IPAddress} {client.ClientId} ServerBase SendCallbackData"); //if (!client.TcpClient.Connected) // serverBase.DisposeClient(client, "SendCallbackData exception"); } finally { //ClientConnectedCallingCount--; } }
internal static async Task RunMethod(ServerBase serverBase, ClientInfo client) { MethodCallbackInfo callback = null; try { byte[] bytes = await client.StreamHelper.ReadBlockToEndAsync(client.ClientStream, CompressMode.None, serverBase.ProviderSetting.MaximumReceiveStreamHeaderBlock); string json = Encoding.UTF8.GetString(bytes); MethodCallInfo callInfo = ServerSerializationHelper.Deserialize <MethodCallInfo>(json, serverBase); //MethodsCallHandler.BeginStreamCallAction?.Invoke(client, guid, serviceName, methodName, values); CallMethodResultInfo <OperationContext> result = await CallMethod(callInfo.ServiceName, callInfo.Guid, callInfo.MethodName, callInfo.Parameters, null, client, null, serverBase, null, null); callback = result.CallbackInfo; } catch (Exception ex) { callback = new MethodCallbackInfo { IsException = true, Data = ServerSerializationHelper.SerializeObject(ex) }; } finally { //MethodsCallHandler.EndStreamCallAction?.Invoke(client, guid, serviceName, methodName, values, jsonResult, exception); } await SendCallbackData(callback, client, serverBase); }
/// <summary> /// after call method from server , client must send callback to server /// </summary> /// <param name="callback">method callback data</param> internal void SendCallbackData(MethodCallbackInfo callback) { string json = JsonConvert.SerializeObject(callback); byte[] bytes = Encoding.UTF8.GetBytes(json); if (SecuritySettings != null) { bytes = EncryptBytes(bytes); } byte[] len = BitConverter.GetBytes(bytes.Length); List <byte> data = new List <byte> { (byte)DataType.ResponseCallMethod, (byte)CompressMode.None }; data.AddRange(len); data.AddRange(bytes); if (data.Count > ProviderSetting.MaximumSendDataBlock) { throw new Exception("SendCallbackData data length is upper than MaximumSendDataBlock"); } GoStreamWriter.WriteToStream(_client.GetStream(), data.ToArray(), IsWebSocket); }
/// <summary> /// this method call when client want to upload file or stream to your server /// </summary> /// <param name="stream">client stream</param> /// <param name="client">client</param> private static async Task DownloadStreamFromClient(ClientInfo client, ServerBase serverBase) { MethodCallbackInfo callback = null; string guid = Guid.NewGuid().ToString(); try { byte[] bytes = await client.StreamHelper.ReadBlockToEndAsync(client.ClientStream, CompressMode.None, serverBase.ProviderSetting.MaximumReceiveStreamHeaderBlock); string json = Encoding.UTF8.GetString(bytes); MethodCallInfo callInfo = ServerSerializationHelper.Deserialize <MethodCallInfo>(json, serverBase); CallMethodResultInfo <OperationContext> result = await CallMethod(callInfo.ServiceName, callInfo.Guid, callInfo.MethodName, callInfo.Parameters, null, client, null, serverBase, null, null); callback = result.CallbackInfo; } catch (IOException ex) { callback = new MethodCallbackInfo(); callback.IsException = true; callback.Data = ServerSerializationHelper.SerializeObject(ex); //return; } catch (Exception ex) { callback = new MethodCallbackInfo(); callback.IsException = true; callback.Data = ServerSerializationHelper.SerializeObject(ex); } finally { } await SendCallbackData(callback, client, serverBase); }
public override StreamInfo RegisterFileToDownload(NetworkStream stream, CompressMode compressMode, ClientInfo client, bool isWebSocket) { var bytes = GoStreamReader.ReadBlockToEnd(stream, compressMode, ProviderSetting.MaximumReceiveDataBlock, isWebSocket); var json = Encoding.UTF8.GetString(bytes); MethodCallInfo callInfo = ServerSerializationHelper.Deserialize <MethodCallInfo>(json); MethodCallbackInfo callback = new MethodCallbackInfo(); callback.Guid = callInfo.Guid; var serviceType = RegisteredServiceTypes[callInfo.ServiceName]; var sessionId = callInfo.Data.ToString(); var clientInfo = (from x in Services.ToArray() where x.Key.SessionId == sessionId select x.Key).FirstOrDefault(); if (clientInfo == null) { throw new Exception("RegisterFile client not found!"); } var service = FindClientServiceByType(clientInfo, serviceType); #if (NETSTANDARD1_6 || NETCOREAPP1_1) var method = serviceType.GetTypeInfo().GetMethod(callInfo.MethodName, RuntimeTypeHelper.GetMethodTypes(serviceType, callInfo).ToArray()); #else var method = serviceType.GetMethod(callInfo.MethodName, RuntimeTypeHelper.GetMethodTypes(serviceType, callInfo).ToArray()); #endif List <object> parameters = new List <object>(); int index = 0; var prms = method.GetParameters(); foreach (var item in callInfo.Parameters) { parameters.Add(ServerSerializationHelper.Deserialize(item.Value, prms[index].ParameterType)); index++; } if (method.ReturnType != typeof(StreamInfo)) { throw new Exception("return type for upload must StreamInfo!"); } else { StreamInfo data = null; data = (StreamInfo)method.Invoke(service, parameters.ToArray()); if (data == null) { throw new Exception($"StreamInfo cannot be null"); } var streamReader = data.Stream; data.Stream = null; callback.Data = ServerSerializationHelper.SerializeObject(data, this); SendCallbackData(callback, client); data.Stream = streamReader; return(data); } }
/// <summary> /// generate segments, part number 0 = no any parts and part number -1 = end of parts /// </summary> /// <param name="segment"></param> /// <returns></returns> public ISegment GenerateAndMixSegments(ISegment segment) { if (segment == null) { throw new Exception("segment is null!"); } if (segment is MethodCallInfo) { MethodCallInfo callInfo = (MethodCallInfo)segment; AddToSegment(callInfo.Guid, callInfo); if (segment.PartNumber == -1) { StringBuilder data = new StringBuilder(); foreach (MethodCallInfo item in Segments[callInfo.Guid]) { data.Append(item.Data.ToString()); } Segments.Remove(callInfo.Guid); return(JsonConvert.DeserializeObject <MethodCallInfo>(data.ToString())); } else { return(null); } } else if (segment is MethodCallbackInfo) { MethodCallbackInfo callbackInfo = (MethodCallbackInfo)segment; AddToSegment(callbackInfo.Guid, callbackInfo); if (segment.PartNumber == -1) { StringBuilder data = new StringBuilder(); foreach (MethodCallbackInfo item in Segments[callbackInfo.Guid]) { data.Append(item.Data.ToString()); } Segments.Remove(callbackInfo.Guid); return(JsonConvert.DeserializeObject <MethodCallbackInfo>(data.ToString())); } else { return(null); } } else { throw new Exception("segment not support: " + segment.ToString()); } }
private static async void SendCallbackData(Task <MethodCallbackInfo> callback, ClientInfo client, ServerBase serverBase) #endif { try { #if (NET35 || NET40) MethodCallbackInfo callbackResult = callback.Result; #else MethodCallbackInfo callbackResult = await callback; #endif string json = ServerSerializationHelper.SerializeObject(callbackResult, serverBase); if (json.Length > 5000) { List <string> listOfParts = GeneratePartsOfData(json); int i = 1; foreach (string item in listOfParts) { MethodCallbackInfo cb = callbackResult.Clone(); cb.PartNumber = i == listOfParts.Count ? (short)-1 : (short)i; cb.Data = item; json = (int)DataType.ResponseCallMethod + "," + (int)CompressMode.None + "/" + ServerSerializationHelper.SerializeObject(cb, serverBase); byte[] result = Encoding.UTF8.GetBytes(json); //if (ClientsSettings.ContainsKey(client)) // result = EncryptBytes(result, client); await client.StreamHelper.WriteToStreamAsync(client.ClientStream, result); i++; } } else { json = (int)DataType.ResponseCallMethod + "," + (int)CompressMode.None + "/" + json; byte[] result = Encoding.UTF8.GetBytes(json); //if (ClientsSettings.ContainsKey(client)) // result = EncryptBytes(result, client); await client.StreamHelper.WriteToStreamAsync(client.ClientStream, result); } } catch (Exception ex) { serverBase.AutoLogger.LogError(ex, $"{client.IPAddress} {client.ClientId} ServerBase SendCallbackData"); //if (!client.TcpClient.Connected) // serverBase.DisposeClient(client, "SendCallbackData exception"); } finally { //ClientConnectedCallingCount--; } }
internal override void ReconnectToUdp(MethodCallInfo callInfo) { AutoLogger.LogText("ReconnectToUdp"); MethodCallbackInfo callback = new MethodCallbackInfo(); callback.Guid = callInfo.Guid; try { SendUdpData(new byte[] { 0 }); } catch (Exception ex) { AutoLogger.LogError(ex, "UdpConnectorBase ReconnectToUdp"); callback.IsException = true; callback.Data = JsonConvert.SerializeObject(ex.ToString()); } SendCallbackData(callback); }
/// <summary> /// call a method of client from server /// </summary> /// <param name="callInfo">method call data</param> internal void CallMethod(MethodCallInfo callInfo) { MethodCallbackInfo callback = new MethodCallbackInfo() { Guid = callInfo.Guid }; try { var service = Callbacks[callInfo.ServiceName].Value; var method = service.GetType().GetMethod(callInfo.MethodName, RuntimeTypeHelper.GetMethodTypes(service.GetType(), callInfo).ToArray()); if (method == null) { throw new Exception($"Method {callInfo.MethodName} from service {callInfo.ServiceName} not found! serviceType: {service.GetType().FullName}"); } List <object> parameters = new List <object>(); int index = 0; foreach (var item in method.GetParameters()) { parameters.Add(JsonConvert.DeserializeObject(callInfo.Parameters[index].Value, item.ParameterType)); index++; } if (method.ReturnType == typeof(void)) { method.Invoke(service, parameters.ToArray()); } else { var data = method.Invoke(service, parameters.ToArray()); callback.Data = data == null ? null : JsonConvert.SerializeObject(data); } } catch (Exception ex) { AutoLogger.LogError(ex, "ConnectorBase CallMethod"); callback.IsException = true; callback.Data = JsonConvert.SerializeObject(ex.ToString()); } SendCallbackData(callback); }
public static async Task StartToReadingClientData(ClientInfo client, ServerBase serverBase) { try { Console.WriteLine($"WebSocket Client Connected: {client.IPAddress}"); Shared.IO.PipeNetworkStream stream = client.ClientStream; while (true) { byte oneByteOfDataType = await client.StreamHelper.ReadOneByteAsync(stream); //type of data DataType dataType = (DataType)oneByteOfDataType; if (dataType == DataType.PingPong) { await client.StreamHelper.WriteToStreamAsync(client.ClientStream, new byte[] { 5 }); continue; } //compress mode of data CompressMode compressMode = (CompressMode)await client.StreamHelper.ReadOneByteAsync(stream); //a server service method called from client if (dataType == DataType.CallMethod) { string json = ""; //if (client.IsOwinClient) //{ // json = await stream.ReadLineAsync("#end"); // if (json.EndsWith("#end")) // json = json.Substring(0, json.Length - 4); //} //else //{ byte[] bytes = await client.StreamHelper.ReadBlockToEndAsync(stream, compressMode, serverBase.ProviderSetting.MaximumReceiveDataBlock); //if (ClientsSettings.ContainsKey(client)) // bytes = DecryptBytes(bytes, client); json = Encoding.UTF8.GetString(bytes); //} MethodCallInfo callInfo = ServerSerializationHelper.Deserialize <MethodCallInfo>(json, serverBase); if (callInfo.PartNumber != 0) { SegmentManager segmentManager = new SegmentManager(); ISegment result = segmentManager.GenerateAndMixSegments(callInfo); if (result != null) { callInfo = (MethodCallInfo)result; } else { continue; } } #if (NET35 || NET40) MethodCallbackInfo callbackResult = CallMethod(callInfo, client, json, serverBase).Result; SendCallbackData(callbackResult, client, serverBase); #else Task <MethodCallbackInfo> callbackResult = CallMethod(callInfo, client, json, serverBase); SendCallbackData(callbackResult, client, serverBase); #endif } //reponse of client method that server called to client else if (dataType == DataType.ResponseCallMethod) { byte[] bytes = await client.StreamHelper.ReadBlockToEndAsync(stream, compressMode, serverBase.ProviderSetting.MaximumReceiveDataBlock); //if (ClientsSettings.ContainsKey(client)) // bytes = DecryptBytes(bytes, client); string json = Encoding.UTF8.GetString(bytes); MethodCallbackInfo callback = ServerSerializationHelper.Deserialize <MethodCallbackInfo>(json, serverBase); if (callback == null) { serverBase.AutoLogger.LogText($"{client.IPAddress} {client.ClientId} callback is null:" + json); } if (callback.PartNumber != 0) { SegmentManager segmentManager = new SegmentManager(); ISegment result = segmentManager.GenerateAndMixSegments(callback); if (result != null) { callback = (MethodCallbackInfo)result; } else { continue; } } if (serverBase.ClientServiceCallMethodsResult.TryGetValue(callback.Guid, out KeyValue <Type, object> resultTask)) { if (callback.IsException) { resultTask.Value.GetType().FindMethod("SetException").Invoke(resultTask.Value, new object[] { new Exception(callback.Data) }); } else { resultTask.Value.GetType().FindMethod("SetResult").Invoke(resultTask.Value, new object[] { ServerSerializationHelper.Deserialize(callback.Data, resultTask.Key, serverBase) }); } } } else if (dataType == DataType.GetServiceDetails) { byte[] bytes = await client.StreamHelper.ReadBlockToEndAsync(stream, compressMode, serverBase.ProviderSetting.MaximumReceiveDataBlock); //if (ClientsSettings.ContainsKey(client)) // bytes = DecryptBytes(bytes, client); string json = Encoding.UTF8.GetString(bytes); string hostUrl = ServerSerializationHelper.Deserialize <string>(json, serverBase); ServerServicesManager serverServicesManager = new ServerServicesManager(); ProviderDetailsInfo detail = serverServicesManager.SendServiceDetail(hostUrl, serverBase); json = ServerSerializationHelper.SerializeObject(detail, serverBase); List <byte> resultBytes = new List <byte> { (byte)DataType.GetServiceDetails, (byte)CompressMode.None }; byte[] jsonBytes = Encoding.UTF8.GetBytes(json); byte[] dataLen = BitConverter.GetBytes(jsonBytes.Length); resultBytes.AddRange(dataLen); resultBytes.AddRange(jsonBytes); await client.StreamHelper.WriteToStreamAsync(client.ClientStream, resultBytes.ToArray()); } else if (dataType == DataType.GetMethodParameterDetails) { byte[] bytes = await client.StreamHelper.ReadBlockToEndAsync(stream, compressMode, serverBase.ProviderSetting.MaximumReceiveDataBlock); //if (ClientsSettings.ContainsKey(client)) // bytes = DecryptBytes(bytes, client); string json = Encoding.UTF8.GetString(bytes); MethodParameterDetails detail = ServerSerializationHelper.Deserialize <MethodParameterDetails>(json, serverBase); if (!serverBase.RegisteredServiceTypes.TryGetValue(detail.ServiceName, out Type serviceType)) { throw new Exception($"{client.IPAddress} {client.ClientId} Service {detail.ServiceName} not found"); } if (serviceType == null) { throw new Exception($"{client.IPAddress} {client.ClientId} serviceType {detail.ServiceName} not found"); } ServerServicesManager serverServicesManager = new ServerServicesManager(); json = serverServicesManager.SendMethodParameterDetail(serviceType, detail, serverBase); List <byte> resultBytes = new List <byte> { (byte)DataType.GetMethodParameterDetails, (byte)CompressMode.None }; byte[] jsonBytes = Encoding.UTF8.GetBytes(json); byte[] dataLen = BitConverter.GetBytes(jsonBytes.Length); resultBytes.AddRange(dataLen); resultBytes.AddRange(jsonBytes); await client.StreamHelper.WriteToStreamAsync(client.ClientStream, resultBytes.ToArray()); } else if (dataType == DataType.GetClientId) { byte[] bytes = Encoding.UTF8.GetBytes(client.ClientId); List <byte> result = new List <byte>(); result.Add((byte)DataType.GetClientId); result.Add((byte)CompressMode.None); result.AddRange(BitConverter.GetBytes(bytes.Length)); result.AddRange(bytes); //if (ClientsSettings.ContainsKey(client)) // bytes = EncryptBytes(bytes, client); if (result.Count > serverBase.ProviderSetting.MaximumSendDataBlock) { throw new Exception($"{client.IPAddress} {client.ClientId} GetClientId data length exceeds MaximumSendDataBlock"); } await client.StreamHelper.WriteToStreamAsync(client.ClientStream, result.ToArray()); } else { //throw new Exception($"Correct DataType Data {dataType}"); serverBase.AutoLogger.LogText($"Correct DataType Data {oneByteOfDataType} {client.ClientId} {client.IPAddress}"); break; } } serverBase.DisposeClient(client, null, "StartToReadingClientData while break"); } catch (Exception ex) { serverBase.AutoLogger.LogError(ex, $"{client.IPAddress} {client.ClientId} ServerBase SignalGoDuplexServiceProvider StartToReadingClientData"); serverBase.DisposeClient(client, null, "SignalGoDuplexServiceProvider StartToReadingClientData exception"); } }
/// <summary> /// start client to reading stream and data from server /// </summary> /// <param name="client"></param> internal void StartToReadingClientData() { AsyncActions.Run(() => { try { var stream = _client.GetStream(); while (true) { //first byte is DataType var dataType = (DataType)stream.ReadByte(); //secound byte is compress mode var compresssMode = (CompressMode)stream.ReadByte(); // server is called client method if (dataType == DataType.CallMethod) { var bytes = GoStreamReader.ReadBlockToEnd(stream, compresssMode, ProviderSetting.MaximumReceiveDataBlock, IsWebSocket); if (SecuritySettings != null) { bytes = DecryptBytes(bytes); } var json = Encoding.UTF8.GetString(bytes); MethodCallInfo callInfo = JsonConvert.DeserializeObject <MethodCallInfo>(json); if (callInfo.Type == MethodType.User) { CallMethod(callInfo); } else if (callInfo.Type == MethodType.SignalGo) { if (callInfo.MethodName == "/MustReconnectUdpServer") { ReconnectToUdp(callInfo); } } } //after client called server method, server response to client else if (dataType == DataType.ResponseCallMethod) { var bytes = GoStreamReader.ReadBlockToEnd(stream, compresssMode, ProviderSetting.MaximumReceiveDataBlock, IsWebSocket); if (SecuritySettings != null) { bytes = DecryptBytes(bytes); } var json = Encoding.UTF8.GetString(bytes); MethodCallbackInfo callback = JsonConvert.DeserializeObject <MethodCallbackInfo>(json); var geted = ConnectorExtension.WaitedMethodsForResponse.TryGetValue(callback.Guid, out KeyValue <AutoResetEvent, MethodCallbackInfo> keyValue); if (geted) { keyValue.Value = callback; keyValue.Key.Set(); } } else if (dataType == DataType.GetServiceDetails) { var bytes = GoStreamReader.ReadBlockToEnd(stream, compresssMode, ProviderSetting.MaximumReceiveDataBlock, IsWebSocket); if (SecuritySettings != null) { bytes = DecryptBytes(bytes); } var json = Encoding.UTF8.GetString(bytes); getServiceDetialResult = JsonConvert.DeserializeObject <ProviderDetailsInfo>(json); if (getServiceDetialResult == null) { getServiceDetialExceptionResult = JsonConvert.DeserializeObject <Exception>(json); } getServiceDetailEvent.Set(); getServiceDetailEvent.Reset(); } else if (dataType == DataType.GetMethodParameterDetails) { var bytes = GoStreamReader.ReadBlockToEnd(stream, compresssMode, ProviderSetting.MaximumReceiveDataBlock, IsWebSocket); if (SecuritySettings != null) { bytes = DecryptBytes(bytes); } var json = Encoding.UTF8.GetString(bytes); getmethodParameterDetailsResult = json; getServiceDetailEvent.Set(); getServiceDetailEvent.Reset(); } else { //incorrect data! :| SignalGo.Shared.Log.AutoLogger.LogText("StartToReadingClientData Incorrect Data!"); Dispose(); break; } } } catch (Exception ex) { SignalGo.Shared.Log.AutoLogger.LogError(ex, "StartToReadingClientData"); Dispose(); } }); }
private static string SendData(this ConnectorBase connector, MethodCallInfo callInfo) { //TryAgain: bool isIgnorePriority = false; try { TaskCompletionSource <MethodCallbackInfo> valueData = new TaskCompletionSource <MethodCallbackInfo>(); CancellationTokenSource ct = new CancellationTokenSource(); ct.Token.Register(() => valueData.TrySetCanceled(), useSynchronizationContext: false); bool added = WaitedMethodsForResponse.TryAdd(callInfo.Guid, valueData); object service = connector.Services.ContainsKey(callInfo.ServiceName) ? connector.Services[callInfo.ServiceName] : null; //#if (PORTABLE) MethodInfo method = service?.GetType().FindMethod(callInfo.MethodName); //#else // var method = service?.GetType().FindMethod(callInfo.MethodName, RuntimeTypeHelper.GetMethodTypes(service.GetType(), callInfo).ToArray()); //#endif // isIgnorePriority = method?.GetCustomAttributes <PriorityCallAttribute>().Count() > 0; connector.SendData(callInfo); MethodCallbackInfo result = valueData.Task.Result; if (result == null) { if (connector.IsDisposed) { throw new ObjectDisposedException("Provider"); } if (!connector.IsConnected) { throw new Exception("client disconnected"); } return(null); } if (result.IsException) { throw new Exception("server exception:" + ClientSerializationHelper.DeserializeObject <string>(result.Data)); } else if (result.IsAccessDenied && result.Data == null) { throw new Exception("server permission denied exception."); } return(result.Data); } catch (Exception ex) { if (connector.IsConnected && !connector.SendPingAndWaitToReceive()) { connector.Disconnect(); } throw ex; } finally { WaitedMethodsForResponse.Remove(callInfo.Guid); } }
/// <summary> /// this method calll when client want to download file or stream from your server /// </summary> /// <param name="stream">client stream</param> /// <param name="client">client</param> private static async Task UploadStreamToClient(ClientInfo client, ServerBase serverBase) { MethodCallbackInfo callback = null; IStreamInfo streamInfo = null; PipeNetworkStream userStream = null; PipeNetworkStream stream = client.ClientStream; bool isCallbackSended = false; try { byte[] bytes = await client.StreamHelper.ReadBlockToEndAsync(client.ClientStream, CompressMode.None, serverBase.ProviderSetting.MaximumReceiveStreamHeaderBlock); string json = Encoding.UTF8.GetString(bytes); MethodCallInfo callInfo = ServerSerializationHelper.Deserialize <MethodCallInfo>(json, serverBase); CallMethodResultInfo <OperationContext> result = await CallMethod(callInfo.ServiceName, callInfo.Guid, callInfo.MethodName, callInfo.Parameters, null, client, null, serverBase, null, null); callback = result.CallbackInfo; streamInfo = result.StreamInfo; userStream = streamInfo.Stream; long len = streamInfo.Length.GetValueOrDefault(); await SendCallbackData(callback, client, serverBase); isCallbackSended = true; long writeLen = 0; while (writeLen < len) { bytes = new byte[1024 * 100]; int readCount = await userStream.ReadAsync(bytes, bytes.Length); byte[] sendBytes = bytes.Take(readCount).ToArray(); await stream.WriteAsync(sendBytes, 0, sendBytes.Length); writeLen += readCount; } userStream.Dispose(); Console.WriteLine("user stream finished"); stream.Dispose(); } catch (Exception ex) { if (streamInfo != null) { streamInfo.Dispose(); } stream.Dispose(); if (userStream != null) { userStream.Dispose(); Console.WriteLine("user stream disposed"); } if (!isCallbackSended && !client.ClientStream.IsClosed) { if (callback == null) { callback = new MethodCallbackInfo(); } callback.IsException = true; callback.Data = ServerSerializationHelper.SerializeObject(ex); await SendCallbackData(callback, client, serverBase); } } finally { //MethodsCallHandler.EndStreamCallAction?.Invoke(client, guid, serviceName, methodName, values, jsonResult, exception); } }