/// <summary> /// Connects to the game server with the given <see cref="ConnectArgs"/>. /// This should be called from the main Unity thread. /// </summary> /// <param name="connectArgs"></param> public void Connect(ConnectArgs connectArgs) { if (isConnecting || IsConnected) { return; } InitializeFields(connectArgs); userId = UnityEngine.Random.Range(0, int.MaxValue); isConnecting = true; RaiseConnecting(); // Instantiating with new TcpClient(url, port) will immediately // try to connect and block. Simply instantiate and connect // in the receive background thread when it starts so the // main Unity thread doesn't hang. client = new TcpClient(); client.Client = null; // Clear the queues when connecting instead of disconnecting // in case you want to still process packets that were received. receivePacketsQueue.Clear(); sendPacketsQueue.Clear(); receivePacketsThread = new Thread(() => { ReceivePacketsThread(connectArgs); }); receivePacketsThread.IsBackground = true; receivePacketsThread.Start(); }
private void TryOpenTcpSocket(ConnectArgs connectArgs) { try { client.Connect(connectArgs.Url, (int)connectArgs.Port); isConnecting = false; client.NoDelay = true; } catch (Exception) { RaiseCouldNotOpenSocket(); throw; } }
private void InitializeFields(ConnectArgs connectArgs) { broadcastCount = 0; abortingMatch = false; userId = UnityEngine.Random.Range(0, int.MaxValue); connectArgs.UserId = userId; Debug.Log($"Your userId={userId}"); matchId = connectArgs.MatchId; Debug.Log($"Connect with matchId={matchId}"); // Can only get the main thread's context from the main thread mainThreadContext = SynchronizationContext.Current; }
private void TryHandshake(ConnectArgs connectArgs) { try { // Write directly to the SSL stream instead of enqueueing // to the send packets queue in order to know immediately // if the server closes the socket because of an invalid // match token. sslStream.Write(PacketFactory.MakeConnectBuffer(userId, connectArgs.MatchId, connectArgs.MatchToken)); } catch (Exception) { // The server will close the socket if the client's match // token was either invalid or its encrypted timestamp // is expired. RaiseServerHandshakeFailed(); throw; } }
private void ReceivePacketsThread(ConnectArgs connectArgs) { // Thread exceptions are silent, so // catching is absolutely required. try { TryOpenTcpSocket(connectArgs); TryOpenSslStream(); TryValidateServer(); TryHandshake(connectArgs); RaiseConnected(); // Start the send packets thread now that there's a connection sendPacketsThread = new Thread(() => { SendPacketsThread(); }); sendPacketsThread.IsBackground = true; sendPacketsThread.Start(); while (true) { byte[] content; if (!ReadPacketBlocking(out content)) { break; } receivePacketsQueue.Enqueue(content); } } catch (SocketException exception) { Debug.Log($"Failed to connect to url={connectArgs.Url} port={connectArgs.Port}\nReason:\n{exception}"); } catch (ThreadInterruptedException) { // Expected if Disconnect() is called } catch (ThreadAbortException) { // Expected if Disconnect() is called } catch (TimeoutException) { RaiseReceiveTimedOut(); } catch (Exception exception) { // something went wrong. probably important. Debug.LogError($"Receive packets thread exception:\n{exception}"); RaiseUnknownError(); } finally { // The send packets thread might be waiting on ManualResetEvent, // interrupt it to ensure that it is stopped. sendPacketsThread?.Interrupt(); // Connect might have failed. isConnecting = false; sslStream?.Close(); client?.Close(); } }