Beispiel #1
0
        /// <summary>
        /// Attempts to complete the connection establish with a minimum of locking to avoid possible deadlocking
        /// </summary>
        /// <param name="remoteConnectionInfo"><see cref="ConnectionInfo"/> corresponding with remoteEndPoint</param>
        /// <param name="possibleClashWithExistingConnection">True if a connection already exists with provided remoteEndPoint</param>
        /// <param name="existingConnection">A reference to an existing connection if it exists</param>
        /// <returns>True if connection is successfully setup, otherwise false</returns>
        private bool ConnectionSetupHandlerFinal(ConnectionInfo remoteConnectionInfo, ref bool possibleClashWithExistingConnection, ref Connection existingConnection)
        {
            lock (NetworkComms.globalDictAndDelegateLocker)
            {
                List <Connection> connectionByEndPoint = NetworkComms.GetExistingConnection(ConnectionInfo.RemoteEndPoint, ConnectionInfo.LocalEndPoint, ConnectionInfo.ConnectionType, ConnectionInfo.ApplicationLayerProtocol);

                //If we no longer have the original endPoint reference (set in the constructor) then the connection must have been closed already
                if (connectionByEndPoint.Count == 0)
                {
                    connectionSetupException    = true;
                    connectionSetupExceptionStr = "Connection setup received after connection closure with " + ConnectionInfo;
                }
                else
                {
                    //COMMENT: As of version 3.0.0 we have allowed loop back connections where the identifier is the same
                    //We need to check for a possible GUID clash
                    //Probability of a clash is approx 0.1% if 1E19 connections are maintained simultaneously (This many connections has not be tested ;))
                    //but hey, we live in a crazy world!
                    //if (remoteConnectionInfo.NetworkIdentifier == NetworkComms.NetworkIdentifier)
                    //{
                    //    connectionSetupException = true;
                    //    connectionSetupExceptionStr = "Remote peer has same network identifier to local, " + remoteConnectionInfo.NetworkIdentifier + ". A real duplication is vanishingly improbable so this exception has probably been thrown because the local and remote application are the same.";
                    //}
                    //else
                    if (connectionByEndPoint[0] != this)
                    {
                        possibleClashWithExistingConnection = true;
                        existingConnection = connectionByEndPoint[0];
                    }
                    else if (connectionByEndPoint[0].ConnectionInfo.NetworkIdentifier != ShortGuid.Empty &&
                             connectionByEndPoint[0].ConnectionInfo.NetworkIdentifier != remoteConnectionInfo.NetworkIdentifier)
                    {
                        //We are in the same connection, so don't need to throw and exception but the remote network identifier
                        //has changed.
                        //This can happen for connection types where the local connection (this) may not have been closed
                        //when the remote peer closed. We need to trigger the connection close delegates with the old info, update
                        //the connection info and then call the establish delegates
                        #region Reset Connection without closing
                        //Call the connection close delegates
                        try
                        {
                            //Almost there
                            //Last thing is to call any connection specific shutdown delegates
                            if (ConnectionSpecificShutdownDelegate != null)
                            {
                                if (NetworkComms.LoggingEnabled)
                                {
                                    NetworkComms.Logger.Debug("Triggered connection specific shutdown delegates with " + ConnectionInfo);
                                }
                                ConnectionSpecificShutdownDelegate(this);
                            }
                        }
                        catch (Exception ex)
                        {
                            LogTools.LogException(ex, "ConnectionSpecificShutdownDelegateError", "Error while executing connection specific shutdown delegates for " + ConnectionInfo + ". Ensure any shutdown exceptions are caught in your own code.");
                        }

                        try
                        {
                            //Last but not least we call any global connection shutdown delegates
                            if (NetworkComms.globalConnectionShutdownDelegates != null)
                            {
                                if (NetworkComms.LoggingEnabled)
                                {
                                    NetworkComms.Logger.Debug("Triggered global shutdown delegates with " + ConnectionInfo);
                                }
                                NetworkComms.globalConnectionShutdownDelegates(this);
                            }
                        }
                        catch (Exception ex)
                        {
                            LogTools.LogException(ex, "GlobalConnectionShutdownDelegateError", "Error while executing global connection shutdown delegates for " + ConnectionInfo + ". Ensure any shutdown exceptions are caught in your own code.");
                        }

                        EndPoint newRemoteEndPoint;
                        if (this.ConnectionInfo.RemoteEndPoint.GetType() == typeof(IPEndPoint) &&
                            remoteConnectionInfo.LocalEndPoint.GetType() == typeof(IPEndPoint))
                        {
                            newRemoteEndPoint = new IPEndPoint(this.ConnectionInfo.RemoteIPEndPoint.Address, remoteConnectionInfo.LocalIPEndPoint.Port);
                        }
                        else
                        {
                            throw new NotImplementedException("ConnectionSetupHandlerFinal not implemented for EndPoints of type " + this.ConnectionInfo.RemoteEndPoint.GetType());
                        }

                        NetworkComms.UpdateConnectionReferenceByEndPoint(this, newRemoteEndPoint, this.ConnectionInfo.LocalEndPoint);

                        ConnectionInfo.UpdateInfoAfterRemoteHandshake(remoteConnectionInfo, newRemoteEndPoint);

                        //Trigger the establish delegates
                        TriggerConnectionEstablishDelegates();
                        #endregion

                        return(true);
                    }
                    else
                    {
                        //Update the connection info
                        //We never change the this.ConnectionInfo.RemoteEndPoint.Address as there might be NAT involved
                        //We may update the port however
                        EndPoint newRemoteEndPoint;
                        if (this is IPConnection)
                        {
                            newRemoteEndPoint = new IPEndPoint(this.ConnectionInfo.RemoteIPEndPoint.Address, remoteConnectionInfo.LocalIPEndPoint.Port);
                        }
#if NET35 || NET4
                        else if (this is BluetoothConnection)
                        {
                            newRemoteEndPoint = ConnectionInfo.RemoteBTEndPoint;
                        }
#endif
                        else
                        {
                            throw new NotImplementedException("ConnectionSetupHandlerFinal not implemented for EndPoints of type " + this.ConnectionInfo.RemoteEndPoint.GetType());
                        }

                        NetworkComms.UpdateConnectionReferenceByEndPoint(this, newRemoteEndPoint, this.ConnectionInfo.LocalEndPoint);

                        ConnectionInfo.UpdateInfoAfterRemoteHandshake(remoteConnectionInfo, newRemoteEndPoint);

                        return(true);
                    }
                }
            }

            return(false);
        }
        /// <summary>
        /// Closes the connection and trigger any associated shutdown delegates.
        /// </summary>
        /// <param name="closeDueToError">Closing a connection due an error possibly requires a few extra steps.</param>
        /// <param name="logLocation">Optional debug parameter.</param>
        public void CloseConnection(bool closeDueToError, int logLocation = 0)
        {
            try
            {
                if (NetworkComms.LoggingEnabled)
                {
                    if (closeDueToError)
                    {
                        NetworkComms.Logger.Debug("Closing connection with " + ConnectionInfo + " due to error from [" + logLocation.ToString() + "].");
                    }
                    else
                    {
                        NetworkComms.Logger.Debug("Closing connection with " + ConnectionInfo + " from [" + logLocation.ToString() + "].");
                    }
                }

                ConnectionInfo.NoteConnectionShutdown();

                //Set possible error cases
                if (closeDueToError)
                {
                    connectionSetupException    = true;
                    connectionSetupExceptionStr = "Connection was closed during setup from [" + logLocation.ToString() + "].";
                }

                //Ensure we are not waiting for a connection to be established if we have died due to error
                connectionSetupWait.Set();

                //Call any connection specific close requirements
                CloseConnectionSpecific(closeDueToError, logLocation);

                try
                {
                    //If we are calling close from the listen thread we are actually in the same thread
                    //We must guarantee the listen thread stops even if that means we need to nuke it
                    //If we did not we may not be able to shutdown properly.
                    if (incomingDataListenThread != null && incomingDataListenThread != Thread.CurrentThread && (incomingDataListenThread.ThreadState == System.Threading.ThreadState.WaitSleepJoin || incomingDataListenThread.ThreadState == System.Threading.ThreadState.Running))
                    {
                        //If we have made it this far we give the ythread a further 50ms to finish before nuking.
                        if (!incomingDataListenThread.Join(50))
                        {
                            incomingDataListenThread.Abort();
                            if (NetworkComms.LoggingEnabled && ConnectionInfo != null)
                            {
                                NetworkComms.Logger.Warn("Incoming data listen thread with " + ConnectionInfo + " aborted.");
                            }
                        }
                    }
                }
                catch (Exception)
                {
                }

                //Close connection my get called multiple times for a given connection depending on the reason for being closed
                bool firstClose = NetworkComms.RemoveConnectionReference(this);

                try
                {
                    //Almost there
                    //Last thing is to call any connection specific shutdown delegates
                    if (firstClose && ConnectionSpecificShutdownDelegate != null)
                    {
                        if (NetworkComms.LoggingEnabled)
                        {
                            NetworkComms.Logger.Debug("Triggered connection specific shutdown delegates with " + ConnectionInfo);
                        }
                        ConnectionSpecificShutdownDelegate(this);
                    }
                }
                catch (Exception ex)
                {
                    NetworkComms.LogError(ex, "ConnectionSpecificShutdownDelegateError", "Error while executing connection specific shutdown delegates for " + ConnectionInfo + ". Ensure any shutdown exceptions are caught in your own code.");
                }

                try
                {
                    //Last but not least we call any global connection shutdown delegates
                    if (firstClose && NetworkComms.globalConnectionShutdownDelegates != null)
                    {
                        if (NetworkComms.LoggingEnabled)
                        {
                            NetworkComms.Logger.Debug("Triggered global shutdown delegates with " + ConnectionInfo);
                        }
                        NetworkComms.globalConnectionShutdownDelegates(this);
                    }
                }
                catch (Exception ex)
                {
                    NetworkComms.LogError(ex, "GlobalConnectionShutdownDelegateError", "Error while executing global connection shutdown delegates for " + ConnectionInfo + ". Ensure any shutdown exceptions are caught in your own code.");
                }
            }
            catch (Exception ex)
            {
                if (ex is ThreadAbortException)
                { /*Ignore the threadabort exception if we had to nuke a thread*/
                }
                else
                {
                    NetworkComms.LogError(ex, "NCError_CloseConnection", "Error closing connection with " + ConnectionInfo + ". Close called from " + logLocation.ToString() + (closeDueToError ? " due to error." : "."));
                }

                //We try to rethrow where possible but CloseConnection could very likely be called from within networkComms so we just have to be happy with a log here
            }
        }