private void ReadAsync() { try { while (IsBound) { switch (Read()) { case ReadState.Void: break; case ReadState.Continue: Thread.Sleep(10); break; case ReadState.Disconnect: return; } } } catch (Exception e) { // There was in issue reading or executing from the network so disconnect // TODO: Add more logging here and an exception with an inner exception being // the exception that was thrown BMSLog.LogException(e); //Console.WriteLine("CRASH: " + e.Message); Disconnect(true); } }
/// <summary> /// 无限循环在单独的线程上监听来自所有连接客户端的新数据。 /// readThreadCancel设置为true时,此循环会中断 /// /// Infinite loop listening for new data from all connected clients on a separate thread. /// This loop breaks when readThreadCancel is set to true /// </summary> private void ReadClients() { //故意无限循环 // Intentional infinite loop while (IsBound && !NetWorker.EndingSession) { try { //如果读取已被标记为取消,则从此循环中断开 // If the read has been flagged to be canceled then break from this loop if (readThreadCancel) { return; } //这将遍历所有玩家,因此请确保将锁设置为 //防止来自其他线程的任何更改 // This will loop through all of the players, so make sure to set the lock to // prevent any changes from other threads lock (Players) { for (int i = 0; i < Players.Count; i++) { //如果读取已被标记为取消,则从此循环中断开 // If the read has been flagged to be canceled then break from this loop if (readThreadCancel) { return; } NetworkStream playerStream = null; if (Players[i].IsHost) { continue; } try { lock (Players[i].MutexLock) { //尝试获取客户端流,如果它仍然可用 // Try to get the client stream if it is still available playerStream = Players[i].TcpClientHandle.GetStream(); } } catch { //无法获取客户端的流,因此强制断开连接 //Console.WriteLine("Exception异常:无法为客户端获取流(强制断开连接)“); // Failed to get the stream for the client so forcefully disconnect it //Console.WriteLine("Exception: Failed to get stream for client (Forcefully disconnecting)"); Disconnect(Players[i], true); continue; } //如果播放器不再连接,请确保正确断开连接 // If the player is no longer connected, then make sure to disconnect it properly if (!Players[i].TcpClientHandle.Connected) { Disconnect(Players[i], false); continue; } //如果有任何数据可用,则只有继续阅读此客户端 // Only continue to read for this client if there is any data available for it if (!playerStream.DataAvailable) { continue; } int available = Players[i].TcpClientHandle.Available; try { lock (Players[i].MutexLock) { // 设置消息ping时间 Players[i].Ping(); // 读取消息 ProtoMsg protoMsg = GetNextBytes(playerStream, available, false); //客户端已经告诉服务器它正在断开连接 if (protoMsg.protoId == ProtoId.ConnectionClose) { //确认连接关闭 protoServer.SendConnectionClose(Players[i].TcpClientHandle); Disconnect(Players[i], false); continue; } protoServer.OnMessage(protoMsg, Players[i]); } } catch { //播放器发送无效数据,请断开连接 Disconnect(Players[i], true); } } } //检查所有挂起的断开连接并清理它们 //完成并确定断开连接 CleanupDisconnections(); //睡眠,这样我们就可以从这个线程释放一些CPU Thread.Sleep(10); } catch (Exception ex) { BMSLog.LogException(ex); } } }
/// <summary> /// This will begin the connection for TCP, this is a thread blocking operation until the connection /// is either established or has failed /// </summary> /// <param name="hostAddress">[127.0.0.1] Ip Address to host from</param> /// <param name="port">[15937] Port to allow connections from</param> public void Connect(string hostAddress = "0.0.0.0", ushort port = DEFAULT_PORT) { if (Disposed) { throw new ObjectDisposedException("TCPServer", "This object has been disposed and can not be used to connect, please use a new TCPServer"); } if (string.IsNullOrEmpty(hostAddress)) { throw new BaseNetworkException("An ip address must be specified to bind to. If you are unsure, you can set to 127.0.0.1"); } // Check to see if this server is being bound to a "loopback" address, if so then bind to any, otherwise bind to specified address if (hostAddress == "0.0.0.0" || hostAddress == "localhost") { ipAddress = IPAddress.Any; } else { ipAddress = IPAddress.Parse(hostAddress); } try { // Setup and start the base C# TcpListner listener = new TcpListener(ipAddress, port); //listener.Start(); Me = new NetworkingPlayer(ServerPlayerCounter++, "0.0.0.0", true, listener, this); Me.InstanceGuid = InstanceGuid.ToString(); // Create the thread that will be listening for clients and start its execution //Thread connectionThread = new Thread(new ThreadStart(ListenForConnections)); //connectionThread.Start(); //Task.Queue(ListenForConnections); listener.Start(); listener.BeginAcceptTcpClient(ListenForConnections, listener); //在成功绑定的结果中执行任何通用初始化 // Do any generic initialization in result of the successful bind OnBindSuccessful(); //创建将监听来自连接客户端的新数据并开始执行的线程 // Create the thread that will be listening for new data from connected clients and start its execution Task.Queue(ReadClients); // 创建将检查播放器超时的线程 // Create the thread that will check for player timeouts Task.Queue(() => { // TODO ZF 关闭检测超时 //commonServerLogic.CheckClientTimeout((player) => //{ // Disconnect(player, true); // OnPlayerTimeout(player); // CleanupDisconnections(); //}); }); //让我知道我连接成功 //Let myself know I connected successfully OnPlayerConnected(Me); //将自己设置为连接的客户端 // Set myself as a connected client Me.Connected = true; //设置端口 //Set the port SetPort((ushort)((IPEndPoint)listener.LocalEndpoint).Port); } catch (Exception e) { BMSLog.LogException(e); // Do any generic initialization in result of the binding failure OnBindFailure(); throw new FailedBindingException("Failed to bind to host/port, see inner exception", e); } }