public void Dispose() { if (myClientStream != null) { AutoResetEvent anAllDataRead = new AutoResetEvent(false); EneterThreadPool.QueueUserWorkItem(() => { try { myClientStream.WaitForPipeDrain(); } catch { // This exception occurs when the dispose is called after the timeout. } anAllDataRead.Set(); }); if (!anAllDataRead.WaitOne(1000)) { EneterTrace.Warning(TracedObject + "failed to wait until named pipe is completely read. It will be disposed."); } myClientStream.Close(); myClientStream.Dispose(); myClientStream = null; } }
public void SendMessage(string receiverId, object message) { using (EneterTrace.Entering()) { // Get the message handler Action <object> aMessageHandler = null; using (ThreadLock.Lock(myRegisteredMessageHandlers)) { myRegisteredMessageHandlers.TryGetValue(receiverId, out aMessageHandler); } // If the message handler was found then send the message if (aMessageHandler != null) { Action aHelperCallback = () => { aMessageHandler(message); }; EneterThreadPool.QueueUserWorkItem(aHelperCallback.Invoke); } else { string anError = "The receiver '" + receiverId + "' does not exist."; EneterTrace.Error(anError); throw new InvalidOperationException(anError); } } }
protected override void OnConnectionOpened(object sender, DuplexChannelEventArgs e) { using (EneterTrace.Entering()) { // Note: Following subscribing cannot block the thread processing events from duplex output channel // because a deadlock can occur. Because if the lock would stop this thread other messages could not be processed. EneterThreadPool.QueueUserWorkItem(() => { // Recover remote subscriptions at service. foreach (KeyValuePair <string, RemoteEvent> aRemoteEvent in myRemoteEvents) { // Note: In Java, the following 'lock' section is located in RemoteEvent class. // It is not possible to locate it there in C# because inner class cannot reach methods of outer class. using (ThreadLock.Lock(aRemoteEvent.Value.SubscribeUnsubscribeLock)) { if (aRemoteEvent.Value.Subscribers.Count > 0) { SubscribeAtService(aRemoteEvent.Key); } } } // Forward the event. myThreadDispatcher.Invoke(() => Notify(ConnectionOpened, e)); }); } }
public void OpenConnection() { using (EneterTrace.Entering()) { using (ThreadLock.Lock(myConnectionManipulatorLock)) { if (IsConnected) { string aMessage = TracedObject + ErrorHandler.IsAlreadyConnected; EneterTrace.Error(aMessage); throw new InvalidOperationException(aMessage); } myOutputChannel.ConnectionOpened += OnConnectionOpened; myOutputChannel.ConnectionClosed += OnConnectionClosed; myOutputChannel.ResponseMessageReceived += OnResponseMessageReceived; myConnectionOpeningRequestedToStopFlag = false; myConnectionOpeningEndedEvent.Reset(); // Try open connection in a different thread. myConnectionOpeningActiveFlag = true; EneterThreadPool.QueueUserWorkItem(DoOpenConnection); // Indicate the ConnectionOpened event shall be raised when the connection is really open. myIsConnectionOpenEventPendingFlag = true; // Indicate the connection is open. myConnectionIsOpenFlag = true; } DuplexChannelEventArgs anEvent = new DuplexChannelEventArgs(ChannelId, ResponseReceiverId, ""); Dispatcher.Invoke(() => Notify(ConnectionOffline, anEvent, false)); } }
private void NotifyConnectionRedirected(string clientId, string from, string to) { using (EneterTrace.Entering()) { Action aWaitCallback = () => { using (EneterTrace.Entering()) { if (ConnectionRedirected != null) { try { RedirectEventArgs aMsg = new RedirectEventArgs(clientId, from, to); ConnectionRedirected(this, aMsg); } catch (Exception err) { EneterTrace.Warning(TracedObject + ErrorHandler.DetectedException, err); } } } }; EneterThreadPool.QueueUserWorkItem(aWaitCallback); } }
private void Notify(EventHandler eventHandler) { using (EneterTrace.Entering()) { Action aConnectionOpenedInvoker = () => { using (EneterTrace.Entering()) { try { if (eventHandler != null) { eventHandler(this, new EventArgs()); } } catch (Exception err) { EneterTrace.Warning(TracedObject + ErrorHandler.DetectedException, err); } } }; // Invoke the event in a different thread. EneterThreadPool.QueueUserWorkItem(aConnectionOpenedInvoker); } }
private void OnConnectionClosed(object sender, DuplexChannelEventArgs e) { using (EneterTrace.Entering()) { using (ThreadLock.Lock(myConnectionManipulatorLock)) { // Try to reopen the connection in a different thread. if (!myConnectionOpeningActiveFlag) { myConnectionOpeningActiveFlag = true; // Start openning in another thread. EneterThreadPool.QueueUserWorkItem(DoOpenConnection); } } Notify(ConnectionOffline, e, false); } }
private void NotifyRequestRecieverRemoved(string channelId) { using (EneterTrace.Entering()) { EneterThreadPool.QueueUserWorkItem(() => { if (RequestReceiverRemoved != null) { try { RequestReceiverRemovedEventArgs anEvent = new RequestReceiverRemovedEventArgs(channelId); RequestReceiverRemoved(this, anEvent); } catch (Exception err) { EneterTrace.Warning(TracedObject + ErrorHandler.DetectedException, err); } } }); } }
public void Execute(Action job) { using (ThreadLock.Lock(myJobQueue)) { if (!myIsWorkingThreadRunning) { myIsWorkingThreadRunning = true; EneterThreadPool.QueueUserWorkItem(DoJobs); // If we are tracing then wait until the message about the working thread is ready. if (EneterTrace.DetailLevel == EneterTrace.EDetailLevel.Debug) { myTraceMessageReady.WaitOne(); } } // Trace into which thread it forwards the job. EneterTrace.Debug(myInvokeTraceMessage); myJobQueue.Enqueue(job); } }
private void OnConnectionClosed(object sender, DuplexChannelEventArgs e) { using (EneterTrace.Entering()) { // Notify the disconnection. if (ConnectionClosed != null) { try { ConnectionClosed(this, e); } catch (Exception err) { EneterTrace.Warning(TracedObject + ErrorHandler.DetectedException, err); } } // In order not to block the thread processing this event handler // start the reconnection loop in a different thread. EneterThreadPool.QueueUserWorkItem(DoReconnect); } }
private void NotifyAllRedirectionsFailed() { using (EneterTrace.Entering()) { Action aWaitCallback = () => { using (EneterTrace.Entering()) { if (AllRedirectionsFailed != null) { try { AllRedirectionsFailed(this, new EventArgs()); } catch (Exception err) { EneterTrace.Warning(TracedObject + ErrorHandler.DetectedException, err); } } } }; EneterThreadPool.QueueUserWorkItem(aWaitCallback); } }
public void OpenConnection(Action <MessageContext> responseMessageHandler) { using (EneterTrace.Entering()) { if (responseMessageHandler == null) { throw new ArgumentNullException("responseMessageHandler is null."); } using (ThreadLock.Lock(myConnectionManipulatorLock)) { try { AddressFamily anAddressFamily = (myUri.HostNameType == UriHostNameType.IPv6) ? AddressFamily.InterNetworkV6 : AddressFamily.InterNetwork; myTcpClient = new TcpClient(anAddressFamily); myTcpClient.NoDelay = true; myTcpClient.SendTimeout = mySendTimeout; myTcpClient.ReceiveTimeout = myReceiveTimeout; myTcpClient.SendBufferSize = mySendBuffer; myTcpClient.ReceiveBufferSize = myReceiveBuffer; myTcpClient.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, myReuseAddressFlag); if (myResponseReceivingPort > 0) { IPAddress aDummyIpAddress = anAddressFamily == AddressFamily.InterNetworkV6 ? IPAddress.IPv6Any : IPAddress.Any; myTcpClient.Client.Bind(new IPEndPoint(aDummyIpAddress, myResponseReceivingPort)); } // Note: TcpClient and Socket do not have a possibility to set the connection timeout. // Therefore it must be workerounded a little bit. Exception anException = null; ManualResetEvent aConnectionCompletedEvent = new ManualResetEvent(false); EneterThreadPool.QueueUserWorkItem(() => { try { // This call also resolves host names. myTcpClient.Connect(myUri.Host, myUri.Port); } catch (Exception err) { anException = err; } aConnectionCompletedEvent.Set(); }); if (!aConnectionCompletedEvent.WaitOne(myConnectTimeout)) { throw new TimeoutException(TracedObject + "failed to open connection within " + myConnectTimeout + " ms."); } if (anException != null) { throw anException; } myIpAddress = (myTcpClient.Client.LocalEndPoint != null) ? myTcpClient.Client.LocalEndPoint.ToString() : ""; myClientStream = myClientSecurityFactory.CreateSecurityStreamAndAuthenticate(myTcpClient.GetStream()); // If it shall listen to response messages. myStopReceivingRequestedFlag = false; myResponseMessageHandler = responseMessageHandler; myResponseReceiverThread = new Thread(DoResponseListening); myResponseReceiverThread.Start(); // Wait until thread listening to response messages is running. myListeningToResponsesStartedEvent.WaitOne(1000); byte[] anEncodedMessage = (byte[])myProtocolFormatter.EncodeOpenConnectionMessage(myOutputConnectorAddress); if (anEncodedMessage != null) { myClientStream.Write(anEncodedMessage, 0, anEncodedMessage.Length); } } catch { CloseConnection(); throw; } } } }
public void Invoke(Action workItem) { EneterThreadPool.QueueUserWorkItem(workItem); }
// This method is called when a message from the service is received. // This can be either the response for a request or it can be an event raised in the service. protected override void OnResponseMessageReceived(object sender, DuplexChannelMessageEventArgs e) { using (EneterTrace.Entering()) { RpcMessage aMessage = null; try { aMessage = mySerializer.ForResponseReceiver(e.ResponseReceiverId).Deserialize <RpcMessage>(e.Message); } catch (Exception err) { EneterTrace.Error(TracedObject + "failed to deserialize incoming message.", err); return; } // If it is a response for a call. if (aMessage.Request == ERpcRequest.Response) { EneterTrace.Debug("RETURN FROM RPC RECEIVED"); // Try to find if there is a pending request waiting for the response. RemoteCallContext anRpcContext; using (ThreadLock.Lock(myPendingRemoteCalls)) { myPendingRemoteCalls.TryGetValue(aMessage.Id, out anRpcContext); } if (anRpcContext != null) { if (string.IsNullOrEmpty(aMessage.ErrorType)) { anRpcContext.SerializedReturnValue = aMessage.SerializedReturn; } else { RpcException anException = new RpcException(aMessage.ErrorMessage, aMessage.ErrorType, aMessage.ErrorDetails); anRpcContext.Error = anException; } // Release the pending request. anRpcContext.RpcCompleted.Set(); } } else if (aMessage.Request == ERpcRequest.RaiseEvent) { EneterTrace.Debug("EVENT FROM SERVICE RECEIVED"); if (aMessage.SerializedParams != null && aMessage.SerializedParams.Length > 0) { // Try to raise an event. // The event is raised in its own thread so that the receiving thread is not blocked. // Note: raising an event cannot block handling of response messages because it can block // processing of an RPC response for which the RPC caller thread is waiting. // And if this waiting caller thread is a thread where events are routed and if the routing // of these events is 'blocking' then a deadlock can occur. // Therefore ThreadPool is used. EneterThreadPool.QueueUserWorkItem(() => myThreadDispatcher.Invoke(() => RaiseEvent(aMessage.OperationName, aMessage.SerializedParams[0]))); } else { // Note: this happens if the event is of type EventErgs. // The event is raised in its own thread so that the receiving thread is not blocked. EneterThreadPool.QueueUserWorkItem(() => myThreadDispatcher.Invoke(() => RaiseEvent(aMessage.OperationName, null))); } } else { EneterTrace.Warning(TracedObject + "detected a message with unknown flag number."); } } }
// Note: BeginAcceptTcpClient() hangs in stress tests. Using QueueUserWorkItem has the same performance and is stable. private void DoTcpListening() { using (EneterTrace.Entering()) { try { // Listening loop. while (!myStopListeningRequested) { // Get the connected client. try { TcpClient aTcpClient = myListener.AcceptTcpClient(); // Execute handler in another thread. Action aHandler = () => { bool aHandleConnectionFlag = false; // Check maximum amount of connections. if (myMaxAmountOfConnections > -1) { int aAmountOfConnections = Interlocked.Increment(ref myAmountOfConnections); if (aAmountOfConnections <= myMaxAmountOfConnections) { aHandleConnectionFlag = true; } } else { aHandleConnectionFlag = true; } if (aHandleConnectionFlag) { try { myConnectionHandler(aTcpClient); } catch (Exception err) { EneterTrace.Error(TracedObject + ErrorHandler.ProcessingTcpConnectionFailure, err); } } else { EneterTrace.Warning(TracedObject + "could not open the connection because the number of maximum connections '" + myMaxAmountOfConnections + "' was excedded."); } if (aTcpClient != null) { aTcpClient.Close(); } Interlocked.Decrement(ref myAmountOfConnections); }; EneterThreadPool.QueueUserWorkItem(aHandler); } catch (Exception err) { // If the exception is not caused by closing the socket. if (!myStopListeningRequested) { EneterTrace.Error(TracedObject + ErrorHandler.ProcessingTcpConnectionFailure, err); } } } } catch (Exception err) { EneterTrace.Error(TracedObject + ErrorHandler.FailedInListeningLoop, err); } } }
/// <summary> /// Listens to client requests and put them to the queue from where the working thread takes them /// and notifies the call-backs the pipe input channel. /// </summary> private void DoListening() { using (EneterTrace.Entering()) { try { // Reconnect the server pipe in the loop. // If the client is not available the thread will wait forewer on WaitForConnection(). // When the listening is stopped then the thread is correctly released. while (!myStopListeningRequestFlag) { myPipeServer.WaitForConnection(); DynamicStream aDynamicStream = null; try { aDynamicStream = new DynamicStream(); // Start thread for reading received messages. EneterThreadPool.QueueUserWorkItem(() => { while (!myStopListeningRequestFlag && myPipeServer.IsConnected) { // Read the whole message. try { myMessageHandler(aDynamicStream); } catch (Exception err) { EneterTrace.Warning(TracedObject + ErrorHandler.DetectedException, err); } } aDynamicStream.Close(); }); // Write incoming messages to the dynamic stream from where the reading thread will notify them. if (!myStopListeningRequestFlag && myPipeServer.IsConnected) { // Read the whole message. try { StreamUtil.ReadToEndWithoutCopying(myPipeServer, aDynamicStream); } catch (Exception err) { EneterTrace.Warning(TracedObject + ErrorHandler.DetectedException, err); } } } finally { if (aDynamicStream != null) { // Note: Do not close the dynamic stream here! // There can be data read before the closing and the client can still finish to read them. //aDynamicStream.Close(); // Unblock the reading thread that can be waiting for messages. aDynamicStream.IsBlockingMode = false; } } // Disconnect the client. if (!myStopListeningRequestFlag) { myPipeServer.Disconnect(); } } } catch (Exception err) { // if the error is not caused by closed communication. if (!myStopListeningRequestFlag) { EneterTrace.Error(TracedObject + ErrorHandler.FailedInListeningLoop, err); } } } }
// A response message is received from the request receiver. private void OnResponseMessageReceived(object sender, DuplexChannelMessageEventArgs e) { using (EneterTrace.Entering()) { using (ThreadLock.Lock(myReceiverManipulatorLock)) { // Find the connection associated with the ResponseRecieverId to which this response message // was delivered. TConnection aConnection = myOpenConnections.FirstOrDefault(x => x.DuplexOutputChannel.ResponseReceiverId == e.ResponseReceiverId); if (aConnection == null) { EneterTrace.Warning(TracedObject + "could not find the receiver for the incoming response message."); return; } // Forward the response message to the client. IDuplexInputChannel anInputChannel = AttachedDuplexInputChannel; if (anInputChannel != null) { try { anInputChannel.SendResponseMessage(aConnection.ResponseReceiverId, e.Message); } catch (Exception err) { EneterTrace.Error(TracedObject + ErrorHandler.FailedToSendResponseMessage, err); try { // Forwarding the response to the client failed so disconnect the client. anInputChannel.DisconnectResponseReceiver(aConnection.ResponseReceiverId); } catch (Exception err2) { EneterTrace.Warning(TracedObject + ErrorHandler.FailedToDisconnectResponseReceiver + aConnection.ResponseReceiverId, err2); } myOpenConnections.RemoveWhere(x => { if (x.ResponseReceiverId == aConnection.ResponseReceiverId) { try { x.DuplexOutputChannel.CloseConnection(); } catch (Exception err2) { EneterTrace.Warning(TracedObject + ErrorHandler.FailedToCloseConnection, err2); } x.DuplexOutputChannel.ResponseMessageReceived -= OnResponseMessageReceived; x.DuplexOutputChannel.ConnectionClosed -= OnRequestReceiverClosedConnection; return(true); } return(false); }); EneterThreadPool.QueueUserWorkItem(() => Notify(ResponseReceiverDisconnected, new ResponseReceiverEventArgs(aConnection.ResponseReceiverId, aConnection.SenderAddress))); } } else { EneterTrace.Error(TracedObject + "cannot send the response message when the duplex input channel is not attached."); } } } }
// Handles TCP connection. // It parses the HTTP request of the websocket to get the requested path. // Then it searches the matching PathListeners and calls it to handle the connection. protected override void HandleConnection(TcpClient tcpClient) { using (EneterTrace.Entering()) { try { // Get the data stream. // Note: If SSL then perform the authentication and provide the stream encoding/decoding data. Stream aDataStream = SecurityFactory.CreateSecurityStreamAndAuthenticate(tcpClient.GetStream()); // Receive open websocket communication request. Match anHttpOpenConnectionRegEx = WebSocketFormatter.DecodeOpenConnectionHttpRequest(aDataStream); if (!anHttpOpenConnectionRegEx.Success) { EneterTrace.Warning(TracedObject + "failed to receive open websocket connection request. (incorrect http request)"); byte[] aCloseConnectionResponse = WebSocketFormatter.EncodeCloseFrame(null, 400); aDataStream.Write(aCloseConnectionResponse, 0, aCloseConnectionResponse.Length); return; } // Get http header fields. IDictionary <string, string> aHeaderFields = WebSocketFormatter.GetHttpHeaderFields(anHttpOpenConnectionRegEx); string aSecurityKey; aHeaderFields.TryGetValue("Sec-WebSocket-Key", out aSecurityKey); // If some required header field is missing or has incorrect value. if (!aHeaderFields.ContainsKey("Upgrade") || !aHeaderFields.ContainsKey("Connection") || string.IsNullOrEmpty(aSecurityKey)) { EneterTrace.Warning(TracedObject + "failed to receive open websocket connection request. (missing or incorrect header field)"); byte[] aCloseConnectionResponse = WebSocketFormatter.EncodeCloseFrame(null, 400); aDataStream.Write(aCloseConnectionResponse, 0, aCloseConnectionResponse.Length); return; } // Get the path to identify the end-point. string anIncomingPath = anHttpOpenConnectionRegEx.Groups["path"].Value; if (string.IsNullOrEmpty(anIncomingPath)) { EneterTrace.Warning(TracedObject + "failed to process Websocket request because the path is null or empty string."); byte[] aCloseConnectionResponse = WebSocketFormatter.EncodeCloseFrame(null, 400); aDataStream.Write(aCloseConnectionResponse, 0, aCloseConnectionResponse.Length); return; } // if the incoming path is the whole uri then extract the absolute path. Uri anIncomingUri; Uri.TryCreate(anIncomingPath, UriKind.Absolute, out anIncomingUri); string anAbsolutePath = (anIncomingUri != null) ? anIncomingUri.AbsolutePath : anIncomingPath; // Get handler for that path. Uri aHandlerUri; Action <IWebSocketClientContext> aPathHandler; using (ThreadLock.Lock(myHandlers)) { KeyValuePair <Uri, object> aPair = myHandlers.FirstOrDefault(x => x.Key.AbsolutePath == anAbsolutePath); aPathHandler = aPair.Value as Action <IWebSocketClientContext>; aHandlerUri = aPair.Key; } // If the listener does not exist. if (aPathHandler == null) { EneterTrace.Warning(TracedObject + "does not listen to " + anIncomingPath); byte[] aCloseConnectionResponse = WebSocketFormatter.EncodeCloseFrame(null, 404); aDataStream.Write(aCloseConnectionResponse, 0, aCloseConnectionResponse.Length); return; } // Response that the connection is accepted. byte[] anOpenConnectionResponse = WebSocketFormatter.EncodeOpenConnectionHttpResponse(aSecurityKey); aDataStream.Write(anOpenConnectionResponse, 0, anOpenConnectionResponse.Length); // Create the context for conecting client. string aRequestUriStr = aHandlerUri.Scheme + "://" + aHandlerUri.Host + ":" + Address.Port + anAbsolutePath + anHttpOpenConnectionRegEx.Groups["query"].Value; Uri aRequestUri = new Uri(aRequestUriStr); WebSocketClientContext aClientContext = new WebSocketClientContext(aRequestUri, aHeaderFields, tcpClient, aDataStream); // Call path handler in a another thread. EneterThreadPool.QueueUserWorkItem(() => { try { aPathHandler(aClientContext); } catch (Exception err) { EneterTrace.Warning(TracedObject + ErrorHandler.DetectedException, err); } }); // Start listening loop in the client context. // The loop will read websocket messages from the underlying tcp connection. // Note: User is responsible to call WebSocketClientContext.CloseConnection() to stop this loop // or the service must close the conneciton. aClientContext.DoRequestListening(); } catch (Exception err) { EneterTrace.Error(TracedObject + "failed to process TCP connection.", err); } } }
/// <summary> /// Opens connection to the websocket server. /// </summary> public void OpenConnection() { using (EneterTrace.Entering()) { using (ThreadLock.Lock(myConnectionManipulatorLock)) { if (IsConnected) { string aMessage = TracedObject + ErrorHandler.IsAlreadyConnected; EneterTrace.Error(aMessage); throw new InvalidOperationException(aMessage); } // If it is needed clear after previous connection if (myTcpClient != null) { try { ClearConnection(false); } catch { // We tried to clean after the previous connection. The exception can be ignored. } } try { myStopReceivingRequestedFlag = false; // Generate the key for this connection. byte[] aWebsocketKey = new byte[16]; myGenerator.NextBytes(aWebsocketKey); string aKey64baseEncoded = Convert.ToBase64String(aWebsocketKey); HeaderFields["Sec-WebSocket-Key"] = aKey64baseEncoded; // Send HTTP request to open the websocket communication. byte[] anOpenRequest = WebSocketFormatter.EncodeOpenConnectionHttpRequest(Uri.AbsolutePath + Uri.Query, HeaderFields); // Open TCP connection. AddressFamily anAddressFamily = (Uri.HostNameType == UriHostNameType.IPv6) ? AddressFamily.InterNetworkV6 : AddressFamily.InterNetwork; myTcpClient = new TcpClient(anAddressFamily); myTcpClient.NoDelay = true; #if !NET35 if (ResponseReceivingPort > 0) { IPAddress aDummyIpAddress = anAddressFamily == AddressFamily.InterNetworkV6 ? IPAddress.IPv6None : IPAddress.None; myTcpClient.Client.Bind(new IPEndPoint(aDummyIpAddress, ResponseReceivingPort)); } #endif myTcpClient.SendTimeout = SendTimeout; myTcpClient.ReceiveTimeout = ReceiveTimeout; // Note: TcpClient and Socket do not have a possibility to set the connection timeout. // There it must be workerounded a little bit. Exception anException = null; ManualResetEvent aConnectionCompletedEvent = new ManualResetEvent(false); EneterThreadPool.QueueUserWorkItem(() => { try { // This call also resolves the host name. myTcpClient.Connect(Uri.Host, Uri.Port); } catch (Exception err) { anException = err; } aConnectionCompletedEvent.Set(); }); if (!aConnectionCompletedEvent.WaitOne(ConnectTimeout)) { throw new TimeoutException(TracedObject + "failed to open connection within " + ConnectTimeout + " ms."); } if (anException != null) { throw anException; } // If SSL then authentication is performed and security stream is provided. myClientStream = mySecurityFactory.CreateSecurityStreamAndAuthenticate(myTcpClient.GetStream()); // Send HTTP request opening the connection. myClientStream.Write(anOpenRequest, 0, anOpenRequest.Length); // Get HTTP response and check if the communication was open. Match anHttpResponseRegEx = WebSocketFormatter.DecodeOpenConnectionHttpResponse(myClientStream); ValidateOpenConnectionResponse(anHttpResponseRegEx, aWebsocketKey); // If somebody is subscribed to receive some response messages then // the bidirectional communication is needed and the listening thread must be activated. if (IsResponseSubscribed) { ActivateResponseListening(); } else { // Nobody is subscribed so delegate the responsibility to start listening threads // to the point when somebody subscribes to receive some response messages like // CloseConnection, Pong, MessageReceived. myResponsibleForActivatingListening = EResponseListeningResponsible.EventSubscription; } // Notify opening the websocket connection. // Note: the notification is executed from a different thread. Notify(ConnectionOpened); } catch (Exception err) { try { ClearConnection(false); } catch { } EneterTrace.Error(TracedObject + ErrorHandler.FailedToOpenConnection, err); throw; } } } }