Ejemplo n.º 1
0
        // the listener thread's listen function
        // note: no maxConnections parameter. high level API should handle that.
        //       (Transport can't send a 'too full' message anyway)
        void Listen(int port)
        {
            // absolutely must wrap with try/catch, otherwise thread
            // exceptions are silent
            try
            {
                // start listener on all IPv4 and IPv6 address via .Create
                listener = TcpListener.Create(port);
                listener.Server.NoDelay        = NoDelay;
                listener.Server.SendTimeout    = SendTimeout;
                listener.Server.ReceiveTimeout = ReceiveTimeout;
                listener.Start();
                Log.Info("Server: listening port=" + port);

                // keep accepting new clients
                while (true)
                {
                    try
                    {
                        // wait and accept new client
                        // note: 'using' sucks here because it will try to
                        // dispose after thread was started but we still need it
                        // in the thread
                        TcpClient client = listener.AcceptTcpClient();

                        // set socket options
                        client.NoDelay        = NoDelay;
                        client.SendTimeout    = SendTimeout;
                        client.ReceiveTimeout = ReceiveTimeout;

                        // generate the next connection id (thread safely)
                        int connectionId = NextConnectionId();

                        // add to dict immediately
                        ConnectionState connection = new ConnectionState(client, MaxMessageSize);
                        clients[connectionId] = connection;

                        // spawn a send thread for each client
                        Thread sendThread = new Thread(() =>
                        {
                            // wrap in try-catch, otherwise Thread exceptions
                            // are silent
                            try
                            {
                                // run the send loop
                                // IMPORTANT: DO NOT SHARE STATE ACROSS MULTIPLE THREADS!
                                ThreadFunctions.SendLoop(connectionId, client, connection.sendPipe, connection.sendPending);
                            }
                            catch (ThreadAbortException)
                            {
                                // happens on stop. don't log anything.
                                // (we catch it in SendLoop too, but it still gets
                                //  through to here when aborting. don't show an
                                //  error.)
                            }
                            catch (Exception exception)
                            {
                                Log.Error("Server send thread exception: " + exception);
                            }
                        });
                        sendThread.IsBackground = true;
                        sendThread.Start();

                        // spawn a receive thread for each client
                        Thread receiveThread = new Thread(() =>
                        {
                            // wrap in try-catch, otherwise Thread exceptions
                            // are silent
                            try
                            {
                                // run the receive loop
                                // (receive pipe is shared across all loops)
                                ThreadFunctions.ReceiveLoop(connectionId, client, MaxMessageSize, receivePipe, ReceiveQueueLimit);

                                // IMPORTANT: do NOT remove from clients after the
                                // thread ends. need to do it in Tick() so that the
                                // disconnect event in the pipe is still processed.
                                // (removing client immediately would mean that the
                                //  pipe is lost and the disconnect event is never
                                //  processed)

                                // sendthread might be waiting on ManualResetEvent,
                                // so let's make sure to end it if the connection
                                // closed.
                                // otherwise the send thread would only end if it's
                                // actually sending data while the connection is
                                // closed.
                                sendThread.Interrupt();
                            }
                            catch (Exception exception)
                            {
                                Log.Error("Server client thread exception: " + exception);
                            }
                        });
                        receiveThread.IsBackground = true;
                        receiveThread.Start();
                    }
                    catch (SocketException exception)
                    {
                        if (exception.ErrorCode == 10035) // WSAEWOULDBLOCK, don't stop server thread
                        {
                            //Log.Info ("Caught WSAEWOULDBLOCK, yielding and retrying later...");
                            Thread.Yield();
                        }
                        else
                        {
                            throw exception;
                        }
                    }
                }
            }
            catch (ThreadAbortException exception)
            {
                // UnityEditor causes AbortException if thread is still
                // running when we press Play again next time. that's okay.
                Log.Info("Server thread aborted. That's okay. " + exception);
            }
            catch (SocketException exception)
            {
                // calling StopServer will interrupt this thread with a
                // 'SocketException: interrupted'. that's okay.
                Log.Info("Server Thread stopped. That's okay. " + exception);
            }
            catch (Exception exception)
            {
                // something went wrong. probably important.
                Log.Error("Server Exception: " + exception);
            }
        }
Ejemplo n.º 2
0
        // the thread function
        // STATIC to avoid sharing state!
        // => pass ClientState object. a new one is created for each new thread!
        // => avoids data races where an old dieing thread might still modify
        //    the current thread's state :/
        static void ReceiveThreadFunction(ClientConnectionState state, string ip, int port, int MaxMessageSize, bool NoDelay, int SendTimeout, int ReceiveTimeout, int ReceiveQueueLimit)

        {
            Thread sendThread = null;

            // absolutely must wrap with try/catch, otherwise thread
            // exceptions are silent
            try
            {
                // connect (blocking)
                state.client.Connect(ip, port);
                state.Connecting = false; // volatile!

                // set socket options after the socket was created in Connect()
                // (not after the constructor because we clear the socket there)
                state.client.NoDelay        = NoDelay;
                state.client.SendTimeout    = SendTimeout;
                state.client.ReceiveTimeout = ReceiveTimeout;

                // start send thread only after connected
                // IMPORTANT: DO NOT SHARE STATE ACROSS MULTIPLE THREADS!
                sendThread = new Thread(() => { ThreadFunctions.SendLoop(0, state.client, state.sendPipe, state.sendPending); });
                sendThread.IsBackground = true;
                sendThread.Start();

                // run the receive loop
                // (receive pipe is shared across all loops)
                ThreadFunctions.ReceiveLoop(0, state.client, MaxMessageSize, state.receivePipe, ReceiveQueueLimit);
            }
            catch (SocketException exception)
            {
                // this happens if (for example) the ip address is correct
                // but there is no server running on that ip/port
                Log.Info("Client Recv: failed to connect to ip=" + ip + " port=" + port + " reason=" + exception);

                // add 'Disconnected' event to receive pipe so that the caller
                // knows that the Connect failed. otherwise they will never know
                state.receivePipe.Enqueue(0, EventType.Disconnected, default);
            }
            catch (ThreadInterruptedException)
            {
                // expected if Disconnect() aborts it
            }
            catch (ThreadAbortException)
            {
                // expected if Disconnect() aborts it
            }
            catch (ObjectDisposedException)
            {
                // expected if Disconnect() aborts it and disposed the client
                // while ReceiveThread is in a blocking Connect() call
            }
            catch (Exception exception)
            {
                // something went wrong. probably important.
                Log.Error("Client Recv Exception: " + exception);
            }

            // sendthread might be waiting on ManualResetEvent,
            // so let's make sure to end it if the connection
            // closed.
            // otherwise the send thread would only end if it's
            // actually sending data while the connection is
            // closed.
            sendThread?.Interrupt();

            // Connect might have failed. thread might have been closed.
            // let's reset connecting state no matter what.
            state.Connecting = false;

            // if we got here then we are done. ReceiveLoop cleans up already,
            // but we may never get there if connect fails. so let's clean up
            // here too.
            state.client?.Close();
        }
Ejemplo n.º 3
0
        // the thread function
        // STATIC to avoid sharing state!
        // => pass ClientState object. a new one is created for each new thread!
        // => avoids data races where an old dieing thread might still modify
        //    the current thread's state :/
        static void ReceiveThreadFunction(ClientState state, string ip, int port, int MaxMessageSize, bool NoDelay, int SendTimeout, int QueueLimit)
        {
            Thread sendThread = null;

            // absolutely must wrap with try/catch, otherwise thread
            // exceptions are silent
            try
            {
                // connect (blocking)
                client.Connect(ip, port);

                Stream stream = client.GetStream();

                if (_Encrypted)
                {
                    RemoteCertificateValidationCallback trustCert = (object sender, X509Certificate x509Certificate,
                                                                     X509Chain x509Chain, SslPolicyErrors policyErrors) =>
                    {
                        if (_AcceptSelfSignedCert)
                        {
                            // All certificates are accepted
                            return(true);
                        }
                        else
                        {
                            if (policyErrors == SslPolicyErrors.None)
                            {
                                return(true);
                            }
                            else
                            {
                                return(false);
                            }
                        }
                    };

                    SslStream encryptedStream = new SslStream(client.GetStream(), false, trustCert, null);
                    stream = encryptedStream;

                    encryptedStream.AuthenticateAsClient(ip);
                }

                _Connecting = false;
                state.client.Connect(ip, port);
                state.Connecting = false; // volatile!

                // set socket options after the socket was created in Connect()
                // (not after the constructor because we clear the socket there)
                state.client.NoDelay     = NoDelay;
                state.client.SendTimeout = SendTimeout;

                // start send thread only after connected
                // IMPORTANT: DO NOT SHARE STATE ACROSS MULTIPLE THREADS!
                sendThread = new Thread(() => { ThreadFunctions.SendLoop(0, state.client, state.sendPipe, state.sendPending); });
                sendThread.IsBackground = true;
                sendThread.Start();

                // run the receive loop
                // IMPORTANT: DO NOT SHARE STATE ACROSS MULTIPLE THREADS!
                ThreadFunctions.ReceiveLoop(0, state.client, MaxMessageSize, state.receivePipe, QueueLimit);
            }
            catch (SocketException exception)
            {
                // this happens if (for example) the ip address is correct
                // but there is no server running on that ip/port
                Log.Info("Client Recv: failed to connect to ip=" + ip + " port=" + port + " reason=" + exception);

                // set 'Disconnected' event to receive pipe so that the caller
                // knows that the Connect failed. otherwise they will never know
                state.receivePipe.SetDisconnected();
            }
            catch (ThreadInterruptedException)
            {
                // expected if Disconnect() aborts it
            }
            catch (ThreadAbortException)
            {
                // expected if Disconnect() aborts it
            }
            catch (ObjectDisposedException)
            {
                // expected if Disconnect() aborts it and disposed the client
                // while ReceiveThread is in a blocking Connect() call
            }
            catch (Exception exception)
            {
                // something went wrong. probably important.
                Log.Error("Client Recv Exception: " + exception);
            }

            // sendthread might be waiting on ManualResetEvent,
            // so let's make sure to end it if the connection
            // closed.
            // otherwise the send thread would only end if it's
            // actually sending data while the connection is
            // closed.
            sendThread?.Interrupt();

            // Connect might have failed. thread might have been closed.
            // let's reset connecting state no matter what.
            state.Connecting = false;

            // if we got here then we are done. ReceiveLoop cleans up already,
            // but we may never get there if connect fails. so let's clean up
            // here too.
            state.client?.Close();
        }
Ejemplo n.º 4
0
        // the listener thread's listen function
        // note: no maxConnections parameter. high level API should handle that.
        //       (Transport can't send a 'too full' message anyway)
        void Listen(int port)
        {
            // absolutely must wrap with try/catch, otherwise thread
            // exceptions are silent
            try
            {
                // start listener on all IPv4 and IPv6 address via .Create
                listener = TcpListener.Create(port);
                listener.Server.NoDelay = NoDelay;
                // IMPORTANT: do not set send/receive timeouts on listener.
                // On linux setting the recv timeout will cause the blocking
                // Accept call to timeout with EACCEPT (which mono interprets
                // as EWOULDBLOCK).
                // https://stackoverflow.com/questions/1917814/eagain-error-for-accept-on-blocking-socket/1918118#1918118
                // => fixes https://github.com/vis2k/Mirror/issues/2695
                //
                // listener.Server.SendTimeout = SendTimeout;
                // listener.Server.ReceiveTimeout = ReceiveTimeout;
                listener.Start();
                Log.Info("Server: listening port=" + port);

                // keep accepting new clients
                while (true)
                {
                    // wait and accept new client
                    // note: 'using' sucks here because it will try to
                    // dispose after thread was started but we still need it
                    // in the thread
                    TcpClient client = listener.AcceptTcpClient();

                    // set socket options
                    client.NoDelay = NoDelay;
                    //client.SendTimeout = SendTimeout;
                    //client.ReceiveTimeout = ReceiveTimeout;

                    // generate the next connection id (thread safely)
                    int connectionId = NextConnectionId();

                    // add to dict immediately
                    var connection = new ChicasPlayer(client, MaxMessageSize);
                    connection.OwnerID    = connectionId;
                    clients[connectionId] = connection;

                    // spawn a send thread for each client
                    Thread sendThread = new Thread(() =>
                    {
                        // wrap in try-catch, otherwise Thread exceptions
                        // are silent
                        try
                        {
                            // run the send loop
                            // IMPORTANT: DO NOT SHARE STATE ACROSS MULTIPLE THREADS!
                            ThreadFunctions.SendLoop(connectionId, client, connection.sendPipe, connection.sendPending);
                        }
                        catch (ThreadAbortException)
                        {
                            // happens on stop. don't log anything.
                            // (we catch it in SendLoop too, but it still gets
                            //  through to here when aborting. don't show an
                            //  error.)
                        }
                        catch (Exception exception)
                        {
                            Log.Error("Server send thread exception: " + exception);
                        }
                    });
                    sendThread.IsBackground = true;
                    sendThread.Start();

                    // spawn a receive thread for each client
                    Thread receiveThread = new Thread(() =>
                    {
                        // wrap in try-catch, otherwise Thread exceptions
                        // are silent
                        try
                        {
                            // run the receive loop
                            // (receive pipe is shared across all loops)
                            ThreadFunctions.ReceiveLoop(connectionId, client, MaxMessageSize, receivePipe, ReceiveQueueLimit);

                            // IMPORTANT: do NOT remove from clients after the
                            // thread ends. need to do it in Tick() so that the
                            // disconnect event in the pipe is still processed.
                            // (removing client immediately would mean that the
                            //  pipe is lost and the disconnect event is never
                            //  processed)

                            // sendthread might be waiting on ManualResetEvent,
                            // so let's make sure to end it if the connection
                            // closed.
                            // otherwise the send thread would only end if it's
                            // actually sending data while the connection is
                            // closed.
                            sendThread.Interrupt();
                        }
                        catch (Exception exception)
                        {
                            Log.Error("Server client thread exception: " + exception);
                        }
                    });
                    receiveThread.IsBackground = true;
                    receiveThread.Start();
                }
            }
            catch (ThreadAbortException exception)
            {
                // UnityEditor causes AbortException if thread is still
                // running when we press Play again next time. that's okay.
                Log.Info("Server thread aborted. That's okay. " + exception);
            }
            catch (SocketException exception)
            {
                // calling StopServer will interrupt this thread with a
                // 'SocketException: interrupted'. that's okay.
                Log.Info("Server Thread stopped. That's okay. " + exception);
            }
            catch (Exception exception)
            {
                // something went wrong. probably important.
                Log.Error("Server Exception: " + exception);
            }
        }
Ejemplo n.º 5
0
        // the listener thread's listen function
        // note: no maxConnections parameter. high level API should handle that.
        //       (Transport can't send a 'too full' message anyway)
        void Listen(int port)
        {
            // absolutely must wrap with try/catch, otherwise thread
            // exceptions are silent
            try
            {
                // start listener on all IPv4 and IPv6 address via .Create
                listener = TcpListener.Create(port);
                listener.Server.NoDelay = NoDelay;
                listener.Server.SendTimeout = SendTimeout;
                listener.Start();
                Log.Info("Server: listening port=" + port);

                X509Certificate cert = null;
                bool selfSignedCert = false;
                
                if (Encrypted)
                {
                    if (CertFile == null)
                    {
                        // Create a new self-signed certificate
                        selfSignedCert = true;
                        cert =
                            new X509Certificate2Builder
                            {
                                SubjectName = string.Format("CN={0}", ((IPEndPoint)listener.LocalEndpoint).Address.ToString())
                            }.Build();
                    }
                    else
                    {
                        cert = X509Certificate.CreateFromCertFile(CertFile);
                        selfSignedCert = false;
                    }
                }

                // keep accepting new clients
                while (true)
                {
                    // wait and accept new client
                    // note: 'using' sucks here because it will try to
                    // dispose after thread was started but we still need it
                    // in the thread
                    TcpClient client = listener.AcceptTcpClient();

                    // set socket options
                    client.NoDelay = NoDelay;
                    client.SendTimeout = SendTimeout;

                    // generate the next connection id (thread safely)
                    int connectionId = NextConnectionId();

                    // add to dict immediately
                    ConnectionState connection = new ConnectionState(client, MaxMessageSize);
                    clients[connectionId] = connection;

                    Stream stream = client.GetStream();
                    
                    Thread sslAuthenticator = null;
                    if (Encrypted)
                    {
                        RemoteCertificateValidationCallback trustCert = (object sender, X509Certificate x509Certificate,
                            X509Chain x509Chain, SslPolicyErrors policyErrors) =>
                        {
                            if (selfSignedCert)
                            {
                                // All certificates are accepted
                                return true;
                            }
                            else
                            {
                                if (policyErrors == SslPolicyErrors.None)
                                {
                                    return true;
                                }
                                else
                                {
                                    return false;
                                }
                            }
                        };

						SslStream sslStream = new SslStream(client.GetStream(), false, trustCert);
                        stream = sslStream;

                        sslAuthenticator = new Thread(() => {
                            try
                            {
                                // Using System.Security.Authentication.SslProtocols.None (the Microsoft recommended parameter which
                                // chooses the highest version of TLS) does not seem to work with Unity. Unity 2018.2 added support
                                // for TLS 1.2 when used with the .NET 4.x runtime, so use preprocessor directives to choose the right protocol
#if UNITY_2018_2_OR_NEWER && NET_4_6
                                System.Security.Authentication.SslProtocols protocol = System.Security.Authentication.SslProtocols.Tls12;
#else
                                System.Security.Authentication.SslProtocols protocol = System.Security.Authentication.SslProtocols.Default;
#endif

                                bool checkCertificateRevocation = !selfSignedCert;
                                sslStream.AuthenticateAsServer(cert, false, protocol, checkCertificateRevocation);
                            }
                            catch (Exception exception)
                            {
                                Logger.LogError("SSL Authenticator exception: " + exception);
                            }
                        });

                        sslAuthenticator.IsBackground = true;
                        sslAuthenticator.Start();
                    }

                    // spawn a send thread for each client
                    Thread sendThread = new Thread(() =>
                    {
                        // wrap in try-catch, otherwise Thread exceptions
                        // are silent
                        try
                        {
                            if (sslAuthenticator != null)
                            {
                                sslAuthenticator.Join();
                            }

                            // run the send loop
                            // IMPORTANT: DO NOT SHARE STATE ACROSS MULTIPLE THREADS!
                            ThreadFunctions.SendLoop(connectionId, client, connection.sendPipe, connection.sendPending);
                        }
                        catch (ThreadAbortException)
                        {
                            // happens on stop. don't log anything.
                            // (we catch it in SendLoop too, but it still gets
                            //  through to here when aborting. don't show an
                            //  error.)
                        }
                        catch (Exception exception)
                        {
                            Log.Error("Server send thread exception: " + exception);
                        }
                    });
                    sendThread.IsBackground = true;
                    sendThread.Start();

                    // spawn a receive thread for each client
                    Thread receiveThread = new Thread(() =>
                    {
                        // wrap in try-catch, otherwise Thread exceptions
                        // are silent
                        try
                        {
                            if (sslAuthenticator != null)
                            {
                                sslAuthenticator.Join();
                            }

                            // run the receive loop
                            // IMPORTANT: DO NOT SHARE STATE ACROSS MULTIPLE THREADS!
                            ThreadFunctions.ReceiveLoop(connectionId, client, MaxMessageSize, connection.receivePipe, QueueLimit);

                            // IMPORTANT: do NOT remove from clients after the
                            // thread ends. need to do it in Tick() so that the
                            // disconnect event in the pipe is still processed.
                            // (removing client immediately would mean that the
                            //  pipe is lost and the disconnect event is never
                            //  processed)

                            // sendthread might be waiting on ManualResetEvent,
                            // so let's make sure to end it if the connection
                            // closed.
                            // otherwise the send thread would only end if it's
                            // actually sending data while the connection is
                            // closed.
                            sendThread.Interrupt();
                        }
                        catch (Exception exception)
                        {
                            Log.Error("Server client thread exception: " + exception);
                        }
                    });
                    receiveThread.IsBackground = true;
                    receiveThread.Start();
                }
            }
            catch (ThreadAbortException exception)
            {
                // UnityEditor causes AbortException if thread is still
                // running when we press Play again next time. that's okay.
                Log.Info("Server thread aborted. That's okay. " + exception);
            }
            catch (SocketException exception)
            {
                // calling StopServer will interrupt this thread with a
                // 'SocketException: interrupted'. that's okay.
                Log.Info("Server Thread stopped. That's okay. " + exception);
            }
            catch (Exception exception)
            {
                // something went wrong. probably important.
                Log.Error("Server Exception: " + exception);
            }
        }