Пример #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>
        private UDPConnection(ConnectionInfo connectionInfo, SendReceiveOptions defaultSendReceiveOptions, UDPOptions level, bool listenForIncomingPackets, UDPConnection existingConnection = null)
            : base(connectionInfo, defaultSendReceiveOptions)
        {
            if (NetworkComms.LoggingEnabled)
            {
                NetworkComms.Logger.Trace("Creating new UDPConnection with " + connectionInfo);
            }

            UDPOptions = 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.RemoteEndPoint.Address.Equals(IPAddress.Any) || connectionInfo.RemoteEndPoint.Address.Equals(IPAddress.IPv6Any))
                {
#if WINDOWS_PHONE
                    //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.LocalEndPoint.Address.ToString()), ConnectionInfo.LocalEndPoint.Port.ToString()).AsTask().Wait();
#else
                    //We are creating an unbound endPoint, this is currently the rogue UDP sender and listeners only
                    udpClientThreadSafe = new UdpClientThreadSafe(new UdpClient(ConnectionInfo.LocalEndPoint));
#endif
                }
                else
                {
                    //If this is a specific connection we link to a default end point here
                    isIsolatedUDPConnection = true;

#if WINDOWS_PHONE
                    if (ConnectionInfo.LocalEndPoint == null)
                    {
                        socket = new DatagramSocket();

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

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

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

                        EndpointPair pair = new EndpointPair(new HostName(ConnectionInfo.LocalEndPoint.Address.ToString()), ConnectionInfo.LocalEndPoint.Port.ToString(),
                                                             new HostName(ConnectionInfo.RemoteEndPoint.Address.ToString()), ConnectionInfo.RemoteEndPoint.Port.ToString());

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

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

#if !WINDOWS_PHONE
                //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.RemoteEndPoint.Address.Equals(IPAddress.Any))
                {
                    throw new Exception("If an existing udpClient is provided it must be unbound to a specific remoteEndPoint");
                }

#if WINDOWS_PHONE
                //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.udpClientThreadSafe = existingConnection.udpClientThreadSafe;
#endif
            }

            IPEndPoint localEndPoint;
#if WINDOWS_PHONE
            localEndPoint = new IPEndPoint(IPAddress.Parse(socket.Information.LocalAddress.DisplayName.ToString()), int.Parse(socket.Information.LocalPort));
#else
            localEndPoint = udpClientThreadSafe.LocalEndPoint;
#endif

            //We can update the localEndPoint so that it is correct
            if (ConnectionInfo.LocalEndPoint == null || ConnectionInfo.LocalEndPoint.Port == 0)
            {
                ConnectionInfo.UpdateLocalEndPointInfo(localEndPoint);
            }
        }
Пример #2
0
        /// <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
        }