private static void newListenerInstance_ConnectionReceived(StreamSocketListener sender, StreamSocketListenerConnectionReceivedEventArgs args) { try { var newConnectionInfo = new ConnectionInfo(true, ConnectionType.TCP, new IPEndPoint(IPAddress.Parse(args.Socket.Information.RemoteAddress.DisplayName.ToString()), int.Parse(args.Socket.Information.RemotePort))); TCPConnection.GetConnection(newConnectionInfo, NetworkComms.DefaultSendReceiveOptions, args.Socket, true); } catch (ConfirmationTimeoutException) { //If this exception gets thrown its generally just a client closing a connection almost immediately after creation } catch (CommunicationException) { //If this exception gets thrown its generally just a client closing a connection almost immediately after creation } catch (ConnectionSetupException) { //If we are the server end and we did not pick the incoming connection up then tooo bad! } catch (SocketException) { //If this exception gets thrown its generally just a client closing a connection almost immediately after creation } catch (Exception ex) { //For some odd reason SocketExceptions don't always get caught above, so another check if (ex.GetBaseException().GetType() != typeof(SocketException)) { //Can we catch the socketException by looking at the string error text? if (ex.ToString().StartsWith("System.Net.Sockets.SocketException")) { NetworkComms.LogError(ex, "ConnectionSetupError_SE"); } else { NetworkComms.LogError(ex, "ConnectionSetupError"); } } } }
internal static TCPConnection GetConnection(ConnectionInfo connectionInfo, SendReceiveOptions defaultSendReceiveOptions, TcpClient tcpClient, bool establishIfRequired = true) #endif { connectionInfo.ConnectionType = ConnectionType.TCP; //If we have a tcpClient at this stage we must be serverside #if WINDOWS_PHONE if (socket != null) { connectionInfo.ServerSide = true; } #else if (tcpClient != null) { connectionInfo.ServerSide = true; } #endif bool newConnection = false; TCPConnection connection; lock (NetworkComms.globalDictAndDelegateLocker) { //Check to see if a conneciton already exists, if it does return that connection, if not return a new one if (NetworkComms.ConnectionExists(connectionInfo.RemoteEndPoint, connectionInfo.ConnectionType)) { if (NetworkComms.LoggingEnabled) { NetworkComms.Logger.Trace("Attempted to create new TCPConnection to connectionInfo='" + connectionInfo + "' but there is an existing connection. Existing connection will be returned instead."); } establishIfRequired = false; connection = (TCPConnection)NetworkComms.GetExistingConnection(connectionInfo.RemoteEndPoint, connectionInfo.ConnectionType); } else { if (NetworkComms.LoggingEnabled) { NetworkComms.Logger.Trace("Creating new TCPConnection to connectionInfo='" + connectionInfo + "'." + (establishIfRequired ? " Connection will be established." : " Connection will not be established.")); } if (connectionInfo.ConnectionState == ConnectionState.Establishing) { throw new ConnectionSetupException("Connection state for connection " + connectionInfo + " is marked as establishing. This should only be the case here due to a bug."); } //If an existing connection does not exist but the info we are using suggests it should we need to reset the info //so that it can be reused correctly. This case generally happens when using Comms in the format //TCPConnection.GetConnection(info).SendObject(packetType, objToSend); if (connectionInfo.ConnectionState == ConnectionState.Established || connectionInfo.ConnectionState == ConnectionState.Shutdown) { connectionInfo.ResetConnectionInfo(); } //We add a reference to networkComms for this connection within the constructor #if WINDOWS_PHONE connection = new TCPConnection(connectionInfo, defaultSendReceiveOptions, socket); #else connection = new TCPConnection(connectionInfo, defaultSendReceiveOptions, tcpClient); #endif newConnection = true; } } if (newConnection && establishIfRequired) { connection.EstablishConnection(); } else if (!newConnection) { connection.WaitForConnectionEstablish(NetworkComms.ConnectionEstablishTimeoutMS); } if (!NetworkComms.commsShutdown) { TriggerConnectionKeepAliveThread(); } return(connection); }
/// <summary> /// Establish the connection /// </summary> protected override void EstablishConnectionSpecific() { #if WINDOWS_PHONE if (socket == null) { ConnectSocket(); } //For the local endpoint var localEndPoint = new IPEndPoint(IPAddress.Parse(socket.Information.LocalAddress.CanonicalName.ToString()), int.Parse(socket.Information.LocalPort)); //We should now be able to set the connectionInfo localEndPoint ConnectionInfo.UpdateLocalEndPointInfo(localEndPoint); //Set the outgoing buffer size socket.Control.OutboundBufferSizeInBytes = (uint)NetworkComms.SendBufferSizeBytes; #else if (tcpClient == null) { ConnectSocket(); } //We should now be able to set the connectionInfo localEndPoint ConnectionInfo.UpdateLocalEndPointInfo((IPEndPoint)tcpClient.Client.LocalEndPoint); //We are going to be using the networkStream quite a bit so we pull out a reference once here tcpClientNetworkStream = tcpClient.GetStream(); //When we tell the socket/client to close we want it to do so immediately //this.tcpClient.LingerState = new LingerOption(false, 0); //We need to set the keep alive option otherwise the connection will just die at some random time should we not be using it //NOTE: This did not seem to work reliably so was replaced with the keepAlive packet feature //this.tcpClient.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.KeepAlive, true); tcpClient.ReceiveBufferSize = NetworkComms.ReceiveBufferSizeBytes; tcpClient.SendBufferSize = NetworkComms.SendBufferSizeBytes; //This disables the 'nagle alogrithm' //http://msdn.microsoft.com/en-us/library/system.net.sockets.socket.nodelay.aspx //Basically we may want to send lots of small packets (<200 bytes) and sometimes those are time critical (e.g. when establishing a connection) //If we leave this enabled small packets may never be sent until a suitable send buffer length threshold is passed. i.e. BAD tcpClient.NoDelay = true; tcpClient.Client.NoDelay = true; #endif //Start listening for incoming data StartIncomingDataListen(); //Get a list of existing listeners List <IPEndPoint> existingListeners = TCPConnection.ExistingLocalListenEndPoints(ConnectionInfo.LocalEndPoint.Address); //Select a listener for this connection IPEndPoint selectedExistingListener = null; if (existingListeners.Count > 0) { selectedExistingListener = (existingListeners.Contains(ConnectionInfo.LocalEndPoint) ? ConnectionInfo.LocalEndPoint : existingListeners[0]); } //If we are server side and we have just received an incoming connection we need to return a conneciton id //This id will be used in all future connections from this machine if (ConnectionInfo.ServerSide) { if (selectedExistingListener == null) { throw new ConnectionSetupException("Detected a server side connection when an existing listener was not present."); } if (NetworkComms.LoggingEnabled) { NetworkComms.Logger.Debug("Waiting for client connnectionInfo from " + ConnectionInfo); } //Wait for the client to send its identification if (!connectionSetupWait.WaitOne(NetworkComms.ConnectionEstablishTimeoutMS)) { throw new ConnectionSetupException("Timeout waiting for client connectionInfo with " + ConnectionInfo + ". Connection created at " + ConnectionInfo.ConnectionCreationTime.ToString("HH:mm:ss.fff") + ", its now " + DateTime.Now.ToString("HH:mm:ss.f")); } if (connectionSetupException) { if (NetworkComms.LoggingEnabled) { NetworkComms.Logger.Debug("Connection setup exception. ServerSide with " + ConnectionInfo + ", " + connectionSetupExceptionStr); } throw new ConnectionSetupException("ServerSide. " + connectionSetupExceptionStr); } //Trigger the connection establish delegates before replying to the connection establish base.EstablishConnectionSpecific(); //Once we have the clients id we send our own SendObject(Enum.GetName(typeof(ReservedPacketType), ReservedPacketType.ConnectionSetup), new ConnectionInfo(ConnectionType.TCP, NetworkComms.NetworkIdentifier, new IPEndPoint(ConnectionInfo.RemoteEndPoint.Address, selectedExistingListener.Port), true), NetworkComms.InternalFixedSendReceiveOptions); } else { if (NetworkComms.LoggingEnabled) { NetworkComms.Logger.Debug("Sending connnectionInfo to " + ConnectionInfo); } //As the client we initiated the connection we now forward our local node identifier to the server //If we are listening we include our local listen port as well SendObject(Enum.GetName(typeof(ReservedPacketType), ReservedPacketType.ConnectionSetup), new ConnectionInfo(ConnectionType.TCP, NetworkComms.NetworkIdentifier, new IPEndPoint(ConnectionInfo.RemoteEndPoint.Address, (selectedExistingListener != null ? selectedExistingListener.Port : ConnectionInfo.LocalEndPoint.Port)), selectedExistingListener != null), NetworkComms.InternalFixedSendReceiveOptions); //Wait here for the server end to return its own identifier if (!connectionSetupWait.WaitOne(NetworkComms.ConnectionEstablishTimeoutMS)) { throw new ConnectionSetupException("Timeout waiting for server connnectionInfo from " + ConnectionInfo + ". Connection created at " + ConnectionInfo.ConnectionCreationTime.ToString("HH:mm:ss.fff") + ", its now " + DateTime.Now.ToString("HH:mm:ss.f")); } //If we are client side we can update the localEndPoint for this connection to reflect what the remote end might see if we are also listening if (selectedExistingListener != null) { ConnectionInfo.UpdateLocalEndPointInfo(selectedExistingListener); } if (connectionSetupException) { if (NetworkComms.LoggingEnabled) { NetworkComms.Logger.Debug("Connection setup exception. ClientSide with " + ConnectionInfo + ", " + connectionSetupExceptionStr); } throw new ConnectionSetupException("ClientSide. " + connectionSetupExceptionStr); } //Trigger the connection establish delegates once the server has replied to the connection establish base.EstablishConnectionSpecific(); } #if !WINDOWS_PHONE //Once the connection has been established we may want to re-enable the 'nagle algorithm' used for reducing network congestion (apparently). //By default we leave the nagle algorithm disabled because we want the quick through put when sending small packets if (EnableNagleAlgorithmForNewConnections) { tcpClient.NoDelay = false; tcpClient.Client.NoDelay = false; } #endif }