Internal wrapper around a udpClient object so that we can easily manage usage.
예제 #1
0
        /// <summary>
        /// Internal constructor for UDP connections
        /// </summary>
        /// <param name="connectionInfo"></param>
        /// <param name="defaultSendReceiveOptions"></param>
        /// <param name="level"></param>
        /// <param name="listenForIncomingPackets"></param>
        /// <param name="existingConnection"></param>
        internal UDPConnection(ConnectionInfo connectionInfo, SendReceiveOptions defaultSendReceiveOptions, UDPOptions level, bool listenForIncomingPackets, UDPConnection existingConnection = null)
            : base(connectionInfo, defaultSendReceiveOptions)
        {
            if (connectionInfo.ConnectionType != ConnectionType.UDP)
            {
                throw new ArgumentException("Provided connectionType must be UDP.", "connectionInfo");
            }

            if (NetworkComms.LoggingEnabled)
            {
                NetworkComms.Logger.Trace("Creating new UDPConnection with " + connectionInfo);
            }

            if (connectionInfo.ApplicationLayerProtocol == ApplicationLayerProtocolStatus.Disabled && level != UDPOptions.None)
            {
                throw new ArgumentException("If the application layer protocol has been disabled the provided UDPOptions can only be UDPOptions.None.");
            }

            ConnectionUDPOptions = level;

            if (listenForIncomingPackets && existingConnection != null)
            {
                throw new Exception("Unable to listen for incoming packets if an existing client has been provided. This is to prevent possible multiple accidently listens on the same client.");
            }

            if (existingConnection == null)
            {
                if (connectionInfo.RemoteIPEndPoint.Address.Equals(IPAddress.Any) || connectionInfo.RemoteIPEndPoint.Address.Equals(IPAddress.IPv6Any))
                {
#if WINDOWS_PHONE || NETFX_CORE
                    //We are creating an unbound endPoint, this is currently the rogue UDP sender and listeners only
                    socket = new DatagramSocket();

                    if (listenForIncomingPackets)
                    {
                        socket.MessageReceived += socket_MessageReceived;
                    }

                    socket.BindEndpointAsync(new HostName(ConnectionInfo.LocalIPEndPoint.Address.ToString()), ConnectionInfo.LocalIPEndPoint.Port.ToString()).AsTask().Wait();
#else
                    //We are creating an unbound endPoint, this is currently the rogue UDP sender and listeners only
                    udpClient = new UdpClientWrapper(new UdpClient(ConnectionInfo.LocalIPEndPoint));
#endif
                }
                else
                {
                    //If this is a specific connection we link to a default end point here
                    isIsolatedUDPConnection = true;

#if WINDOWS_PHONE || NETFX_CORE
                    if (ConnectionInfo.LocalEndPoint == null ||
                        (ConnectionInfo.LocalIPEndPoint.Address == IPAddress.Any && connectionInfo.LocalIPEndPoint.Port == 0) ||
                        (ConnectionInfo.LocalIPEndPoint.Address == IPAddress.IPv6Any && connectionInfo.LocalIPEndPoint.Port == 0))
                    {
                        socket = new DatagramSocket();

                        if (listenForIncomingPackets)
                        {
                            socket.MessageReceived += socket_MessageReceived;
                        }

                        socket.ConnectAsync(new HostName(ConnectionInfo.RemoteIPEndPoint.Address.ToString()), ConnectionInfo.RemoteIPEndPoint.Port.ToString()).AsTask().Wait();
                    }
                    else
                    {
                        socket = new DatagramSocket();

                        if (listenForIncomingPackets)
                        {
                            socket.MessageReceived += socket_MessageReceived;
                        }

                        EndpointPair pair = new EndpointPair(new HostName(ConnectionInfo.LocalIPEndPoint.Address.ToString()), ConnectionInfo.LocalIPEndPoint.Port.ToString(),
                                                             new HostName(ConnectionInfo.RemoteIPEndPoint.Address.ToString()), ConnectionInfo.RemoteIPEndPoint.Port.ToString());

                        socket.ConnectAsync(pair).AsTask().Wait();
                    }
#else
                    if (ConnectionInfo.LocalEndPoint == null)
                    {
                        udpClient = new UdpClientWrapper(new UdpClient(ConnectionInfo.RemoteEndPoint.AddressFamily));
                    }
                    else
                    {
                        udpClient = new UdpClientWrapper(new UdpClient(ConnectionInfo.LocalIPEndPoint));
                    }

                    //By calling connect we discard packets from anything other then the provided remoteEndPoint on our localEndPoint
                    udpClient.Connect(ConnectionInfo.RemoteIPEndPoint);
#endif
                }

#if !WINDOWS_PHONE && !NETFX_CORE
                //NAT traversal does not work in .net 2.0
                //Mono does not seem to have implemented AllowNatTraversal method and attempting the below method call will throw an exception
                //if (Type.GetType("Mono.Runtime") == null)
                //Allow NAT traversal by default for all udp clients
                //    udpClientThreadSafe.AllowNatTraversal(true);

                if (listenForIncomingPackets)
                {
                    StartIncomingDataListen();
                }
#endif
            }
            else
            {
                if (!existingConnection.ConnectionInfo.RemoteIPEndPoint.Address.Equals(IPAddress.Any))
                {
                    throw new Exception("If an existing udpClient is provided it must be unbound to a specific remoteEndPoint");
                }

#if WINDOWS_PHONE || NETFX_CORE
                //Using an exiting client allows us to send from the same port as for the provided existing connection
                this.socket = existingConnection.socket;
#else
                //Using an exiting client allows us to send from the same port as for the provided existing connection
                this.udpClient = existingConnection.udpClient;
#endif
            }

            IPEndPoint localEndPoint;
#if WINDOWS_PHONE || NETFX_CORE
            localEndPoint = new IPEndPoint(IPAddress.Parse(socket.Information.LocalAddress.DisplayName.ToString()), int.Parse(socket.Information.LocalPort));
#else
            localEndPoint = udpClient.LocalIPEndPoint;
#endif

            //We can update the localEndPoint so that it is correct
            if (!ConnectionInfo.LocalEndPoint.Equals(localEndPoint))
            {
                //We should now be able to set the connectionInfo localEndPoint
                NetworkComms.UpdateConnectionReferenceByEndPoint(this, ConnectionInfo.RemoteIPEndPoint, localEndPoint);
                ConnectionInfo.UpdateLocalEndPointInfo(localEndPoint);
            }
        }
예제 #2
0
        /// <summary>
        /// Incoming data listen async method
        /// </summary>
        /// <param name="ar">Call back state data</param>
        private void IncomingUDPPacketHandler(IAsyncResult ar)
        {
            try
            {
                UdpClientWrapper client         = (UdpClientWrapper)ar.AsyncState;
                IPEndPoint       remoteEndPoint = new IPEndPoint(IPAddress.None, 0);
                byte[]           receivedBytes  = client.EndReceive(ar, ref remoteEndPoint);

                //Received data after comms shutdown initiated. We should just close the connection
                if (NetworkComms.commsShutdown)
                {
                    CloseConnection(false, -13);
                }

                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");
                        }
                    }
                }

                client.BeginReceive(new AsyncCallback(IncomingUDPPacketHandler), client);
            }
            //On any error here we close the connection
            catch (NullReferenceException)
            {
                CloseConnection(true, 25);
            }
            catch (ArgumentNullException)
            {
                CloseConnection(true, 36);
            }
            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);
            }
        }