Exemple #1
0
        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;
            }
        }
Exemple #2
0
        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);
                }
            }
        }
Exemple #3
0
        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);
            }
        }
Exemple #10
0
        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);
 }
Exemple #14
0
        // 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);
                }
            }
        }
Exemple #19
0
        /// <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;
                    }
                }
            }
        }