Ejemplo n.º 1
0
        /// <summary>
        /// 发送服务器列表Ping请求
        /// </summary>
        /// <param name="hostname">主机名</param>
        /// <param name="port">端口</param>
        /// <returns></returns>
        public ServerListPingResult ServerListPing(string hostname, ushort port)
        {
            if (State != MinecraftClientState.InTitle)
            {
                throw new InvalidOperationException($"invalid client state {State}, it should be {MinecraftClientState.InTitle}.");
            }
            const int timeout    = 10000;
            var       timedOut   = false;
            var       handshaked = false;

            _logger.Info($"Pinging {hostname}, {port}");
            var result = new ServerListPingResult
            {
                Delay = -1
            };
            DateTime        time1;
            TcpClient       client  = null;
            ProtocolAdapter adapter = null;

            try
            {
                client = new TcpClient();
                client.Connect(hostname, port);
                adapter = new ProtocolAdapter(client.GetStream(), PacketBoundTo.Server);
                adapter.Start();
                _ = Task.Run(async() =>  // timeout
                {
                    await Task.Delay(timeout);
                    if (adapter.State != ProtocolState.Closed)
                    {
                        timedOut = true;
                        client.Close();
                        _logger.Warn($"{hostname}:{port} timed out");
                    }
                });
                adapter.WritePacket(new HandshakePacket //handshake
                {
                    NextState       = ProtocolState.Status,
                    ProtocolVersion = _protocolVersion,
                    ServerAddress   = hostname,
                    ServerPort      = port
                });
                adapter.WritePacket(new StatusRequestPacket());
                var statusResponsePacket = (StatusResponsePacket)adapter.ReadPacket();
                handshaked = true;
                result.LoadContent(statusResponsePacket.Content);
                time1 = DateTime.Now;
                adapter.WritePacket(new StatusPingPacket {
                    Payload = time1.ToUnixTimeStamp()
                });
                if ((StatusPongPacket)adapter.ReadPacket() != null)
                {
                    result.Delay = (int)(DateTime.Now - time1).TotalMinutes;
                }
                adapter.Close();
            }
            catch
            {
                if (!handshaked)
                {
                    throw;
                }
            }
            finally
            {
                adapter?.Dispose();
                client?.Dispose();
            }
            if (timedOut)
            {
                _logger.Warn("Pinging timeout");
            }
            return(result);
        }