/// <summary> /// Incoming data listen sync method /// </summary> private void IncomingUDPPacketWorker() { try { while (true) { if (ConnectionInfo.ConnectionState == ConnectionState.Shutdown) { break; } IPEndPoint remoteEndPoint = new IPEndPoint(IPAddress.None, 0); byte[] receivedBytes = udpClient.Receive(ref remoteEndPoint); //Received data after comms shutdown initiated. We should just close the connection if (NetworkComms.commsShutdown) { CloseConnection(false, -14); } if (NetworkComms.LoggingEnabled) { NetworkComms.Logger.Trace("Received " + receivedBytes.Length.ToString() + " bytes via UDP from " + remoteEndPoint.Address + ":" + remoteEndPoint.Port.ToString() + "."); } UDPConnection connection; HandshakeUDPDatagram possibleHandshakeUDPDatagram = new HandshakeUDPDatagram(receivedBytes); if (isIsolatedUDPConnection) { //This connection was created for a specific remoteEndPoint so we can handle the data internally connection = this; } else { ConnectionInfo desiredConnection = new ConnectionInfo(ConnectionType.UDP, remoteEndPoint, udpClient.LocalIPEndPoint, ConnectionInfo.ApplicationLayerProtocol, ConnectionInfo.ConnectionListener); try { //Look for an existing connection, if one does not exist we will create it //This ensures that all further processing knows about the correct endPoint connection = GetConnection(desiredConnection, ConnectionUDPOptions, ConnectionDefaultSendReceiveOptions, false, this, possibleHandshakeUDPDatagram); } catch (ConnectionShutdownException) { if ((ConnectionUDPOptions & UDPOptions.Handshake) == UDPOptions.Handshake) { if (NetworkComms.LoggingEnabled) { NetworkComms.Logger.Trace("Attempted to get connection " + desiredConnection + " but this caused a ConnectionShutdownException. Exception caught and ignored as should only happen if the connection was closed shortly after being created."); } connection = null; } else { throw; } } } if (connection != null && !possibleHandshakeUDPDatagram.DatagramHandled) { //We pass the data off to the specific connection //Lock on the packetbuilder locker as we may receive UDP packets in parallel from this host lock (connection.packetBuilder.Locker) { if (NetworkComms.LoggingEnabled) { NetworkComms.Logger.Trace(" ... " + receivedBytes.Length.ToString() + " bytes added to packetBuilder for " + connection.ConnectionInfo + ". Cached " + connection.packetBuilder.TotalBytesCached.ToString() + " bytes, expecting " + connection.packetBuilder.TotalBytesExpected.ToString() + " bytes."); } connection.packetBuilder.AddPartialPacket(receivedBytes.Length, receivedBytes); if (connection.packetBuilder.TotalBytesCached > 0) { connection.IncomingPacketHandleHandOff(connection.packetBuilder); } if (connection.packetBuilder.TotalPartialPacketCount > 0) { LogTools.LogException(new Exception("Packet builder had " + connection.packetBuilder.TotalBytesCached + " bytes remaining after a call to IncomingPacketHandleHandOff with connection " + connection.ConnectionInfo + ". Until sequenced packets are implemented this indicates a possible error."), "UDPConnectionError"); } } } } } //On any error here we close the connection catch (NullReferenceException) { CloseConnection(true, 20); } catch (ArgumentNullException) { CloseConnection(true, 37); } catch (IOException) { CloseConnection(true, 21); } catch (ObjectDisposedException) { CloseConnection(true, 22); } catch (SocketException) { //Receive may throw a SocketException ErrorCode=10054 after attempting to send a datagram to an unreachable target. //We will try to get around this by ignoring the ICMP packet causing these problems on client creation CloseConnection(true, 23); } catch (InvalidOperationException) { CloseConnection(true, 24); } catch (ConnectionSetupException) { //Can occur if data is received as comms is being shutdown. //Method will attempt to create new connection which will throw ConnectionSetupException. CloseConnection(true, 50); } catch (Exception ex) { LogTools.LogException(ex, "Error_UDPConnectionIncomingPacketHandler"); CloseConnection(true, 41); } //Clear the listen thread object because the thread is about to end incomingDataListenThread = null; if (NetworkComms.LoggingEnabled) { NetworkComms.Logger.Trace("Incoming data listen thread ending for " + ConnectionInfo); } }
/// <summary> /// Internal UDP creation method that performs the necessary tasks /// </summary> /// <param name="connectionInfo"></param> /// <param name="defaultSendReceiveOptions"></param> /// <param name="level"></param> /// <param name="listenForReturnPackets"></param> /// <param name="existingListenerConnection"></param> /// <param name="possibleHandshakeUDPDatagram"></param> /// <param name="establishIfRequired">Will establish the connection, triggering connection establish delegates if a new /// connection is returned</param> /// <returns></returns> internal static UDPConnection GetConnection(ConnectionInfo connectionInfo, UDPOptions level, SendReceiveOptions defaultSendReceiveOptions, bool listenForReturnPackets, UDPConnection existingListenerConnection, HandshakeUDPDatagram possibleHandshakeUDPDatagram, bool establishIfRequired = true) { connectionInfo.ConnectionType = ConnectionType.UDP; bool newConnection = false; UDPConnection connection = null; lock (NetworkComms.globalDictAndDelegateLocker) { List <Connection> existingConnections = NetworkComms.GetExistingConnection(connectionInfo.RemoteIPEndPoint, connectionInfo.LocalIPEndPoint, ConnectionType.UDP, connectionInfo.ApplicationLayerProtocol); if (existingConnections.Count > 0) { connection = (UDPConnection)existingConnections[0]; } else { //If we are listening on what will be the outgoing adaptor we send with that client to ensure if our connection info is handed off we are connectable by others if (existingListenerConnection == null) { try { IPEndPoint localEndPoint = IPTools.BestLocalEndPoint(connectionInfo.RemoteIPEndPoint); //Set the port to 0 so that we match any listener localEndPoint.Port = 0; List <UDPConnectionListener> existingListeners = Connection.ExistingLocalListeners <UDPConnectionListener>(localEndPoint); for (int i = 0; i < existingListeners.Count; i++) { if (existingListeners[i].UDPConnection.ConnectionInfo.ApplicationLayerProtocol == connectionInfo.ApplicationLayerProtocol) { existingListenerConnection = existingListeners[i].UDPConnection; //If we are using an existing listener there is no need to listen for packets listenForReturnPackets = false; //Once we have a matching connection we can break break; } } } catch (Exception) { if (NetworkComms.LoggingEnabled) { NetworkComms.Logger.Trace("Failed to determine preferred existing udpClientListener to " + connectionInfo.RemoteIPEndPoint.Address + ":" + connectionInfo.RemoteIPEndPoint.Port.ToString() + ". Will create an isolated UDP connection instead."); } } } //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 //UDPConnection.GetConnection(info).SendObject(packetType, objToSend); if (connectionInfo.ConnectionState == ConnectionState.Established || connectionInfo.ConnectionState == ConnectionState.Shutdown) { connectionInfo.ResetConnectionInfo(); } connection = new UDPConnection(connectionInfo, defaultSendReceiveOptions, level, listenForReturnPackets, existingListenerConnection); newConnection = true; } } //If we expect a UDP handshake we need to handle incoming datagrams here, if we have it available, // before trying to establish the connection. //This is different for TCP connections because things happen in the reverse order //UDP - Already listening, receive connectionsetup, configure connection //TCP - Receive TCPClient, configure connection, start listening for connectionsetup, wait for connectionsetup // //possibleHandshakeUDPDatagram will only be set when GetConnection() is called from a listener //If multiple threads try to create an outgoing UDP connection to the same endPoint all but the originating //thread will be held on connection.WaitForConnectionEstablish(); if (possibleHandshakeUDPDatagram != null && (connection.ConnectionUDPOptions & UDPOptions.Handshake) == UDPOptions.Handshake) { lock (connection.packetBuilder.Locker) { if (NetworkComms.LoggingEnabled) { NetworkComms.Logger.Trace(" ... " + possibleHandshakeUDPDatagram.DatagramBytes.Length.ToString() + " handshake bytes added to packetBuilder for " + connection.ConnectionInfo + ". Cached " + connection.packetBuilder.TotalBytesCached.ToString() + " bytes, expecting " + connection.packetBuilder.TotalBytesExpected.ToString() + " bytes."); } connection.packetBuilder.AddPartialPacket(possibleHandshakeUDPDatagram.DatagramBytes.Length, possibleHandshakeUDPDatagram.DatagramBytes); if (connection.packetBuilder.TotalBytesCached > 0) { connection.IncomingPacketHandleHandOff(connection.packetBuilder); } } if (connection.packetBuilder.TotalPartialPacketCount > 0) { LogTools.LogException(new Exception("Packet builder had " + connection.packetBuilder.TotalBytesCached + " bytes remaining after a call to IncomingPacketHandleHandOff with connection " + connection.ConnectionInfo + ". Until sequenced packets are implemented this indicates a possible error."), "UDPConnectionError"); } possibleHandshakeUDPDatagram.DatagramHandled = true; } //We must perform the establish outside the lock as for TCP connections if (newConnection && establishIfRequired) { //Call establish on the connection if it is not a rogue sender or listener if (!connectionInfo.RemoteIPEndPoint.Address.Equals(IPAddress.Any) && !connectionInfo.RemoteIPEndPoint.Address.Equals(IPAddress.IPv6Any)) { connection.EstablishConnection(); } } else if (!newConnection) { connection.WaitForConnectionEstablish(NetworkComms.ConnectionEstablishTimeoutMS); } //UDP does not need keep alives //if (!NetworkComms.commsShutdown) // TriggerConnectionKeepAliveThread(); return(connection); }
void socket_MessageReceived(DatagramSocket sender, DatagramSocketMessageReceivedEventArgs args) { try { var stream = args.GetDataStream().AsStreamForRead(); var dataLength = args.GetDataReader().UnconsumedBufferLength; byte[] receivedBytes = new byte[dataLength]; using (MemoryStream mem = new MemoryStream(receivedBytes)) stream.CopyTo(mem); //Received data after comms shutdown initiated. We should just close the connection if (NetworkComms.commsShutdown) { CloseConnection(false, -15); } stream = null; if (NetworkComms.LoggingEnabled) { NetworkComms.Logger.Trace("Received " + receivedBytes.Length + " bytes via UDP from " + args.RemoteAddress + ":" + args.RemotePort + "."); } UDPConnection connection; HandshakeUDPDatagram possibleHandshakeUDPDatagram = new HandshakeUDPDatagram(receivedBytes); if (isIsolatedUDPConnection) { //This connection was created for a specific remoteEndPoint so we can handle the data internally connection = this; } else { IPEndPoint remoteEndPoint = new IPEndPoint(IPAddress.Parse(args.RemoteAddress.DisplayName.ToString()), int.Parse(args.RemotePort)); IPEndPoint localEndPoint = new IPEndPoint(IPAddress.Parse(sender.Information.LocalAddress.DisplayName.ToString()), int.Parse(sender.Information.LocalPort)); ConnectionInfo desiredConnection = new ConnectionInfo(ConnectionType.UDP, remoteEndPoint, localEndPoint, ConnectionInfo.ApplicationLayerProtocol, ConnectionInfo.ConnectionListener); try { //Look for an existing connection, if one does not exist we will create it //This ensures that all further processing knows about the correct endPoint connection = GetConnection(desiredConnection, ConnectionUDPOptions, ConnectionDefaultSendReceiveOptions, false, this, possibleHandshakeUDPDatagram); } catch (ConnectionShutdownException) { if ((ConnectionUDPOptions & UDPOptions.Handshake) == UDPOptions.Handshake) { if (NetworkComms.LoggingEnabled) { NetworkComms.Logger.Trace("Attempted to get connection " + desiredConnection + " but this caused a ConnectionShutdownException. Exception caught and ignored as should only happen if the connection was closed shortly after being created."); } connection = null; } else { throw; } } } if (connection != null && !possibleHandshakeUDPDatagram.DatagramHandled) { //We pass the data off to the specific connection //Lock on the packetbuilder locker as we may receive UDP packets in parallel from this host lock (connection.packetBuilder.Locker) { connection.packetBuilder.AddPartialPacket(receivedBytes.Length, receivedBytes); if (connection.packetBuilder.TotalBytesCached > 0) { connection.IncomingPacketHandleHandOff(connection.packetBuilder); } if (connection.packetBuilder.TotalPartialPacketCount > 0) { LogTools.LogException(new Exception("Packet builder had " + connection.packetBuilder.TotalBytesCached + " bytes remaining after a call to IncomingPacketHandleHandOff with connection " + connection.ConnectionInfo + ". Until sequenced packets are implemented this indicates a possible error."), "UDPConnectionError"); } } } } //On any error here we close the connection catch (NullReferenceException) { CloseConnection(true, 25); } catch (ArgumentNullException) { CloseConnection(true, 38); } catch (IOException) { CloseConnection(true, 26); } catch (ObjectDisposedException) { CloseConnection(true, 27); } catch (SocketException) { //Receive may throw a SocketException ErrorCode=10054 after attempting to send a datagram to an unreachable target. //We will try to get around this by ignoring the ICMP packet causing these problems on client creation CloseConnection(true, 28); } catch (InvalidOperationException) { CloseConnection(true, 29); } catch (ConnectionSetupException) { //Can occur if data is received as comms is being shutdown. //Method will attempt to create new connection which will throw ConnectionSetupException. CloseConnection(true, 50); } catch (Exception ex) { LogTools.LogException(ex, "Error_UDPConnectionIncomingPacketHandler"); CloseConnection(true, 30); } }