使用UDP套接字检测Minecraft基岩版服务器在线状态异常排查
问题分析与修复方案
你的函数始终返回true的核心原因是UDP协议的无连接特性,加上代码逻辑没有正确验证服务器的响应,具体问题点如下:
- UDP套接字创建不代表服务器在线:
fsockopen('udp://...')只要本地系统能成功创建UDP套接字,就会返回资源对象,完全不检查目标服务器是否存在或可达。哪怕目标IP/端口是无效的,这个调用也大概率返回非false。 - fwrite成功不代表服务器收到数据:UDP没有传输确认机制,
fwrite只是把数据写入本地操作系统的发送缓冲区,只要缓冲区有空间就会返回成功,和服务器是否在线、是否收到数据毫无关系。 - 缺少响应校验逻辑:MCPE服务器的在线检测需要发送
UNCONNECTED_PING包后,等待并验证服务器返回的UNCONNECTED_PONG包。你的代码只发了ping,完全没处理响应,直接判定在线。
修复后的代码示例
下面的代码会发送标准的MCPE无连接ping,并等待服务器的响应,只有收到符合格式的pong包才返回true:
/** * @return bool */ public function checkUDP(): bool { $udp = false; if ($result = resolveSRV($this->address, $this->port, 'udp')) { $this->address = $result['address']; $this->port = $result['port']; } // 创建UDP套接字 $socket = @socket_create(AF_INET, SOCK_DGRAM, SOL_UDP); if (!$socket) { return false; } // 设置超时(秒+微秒) socket_set_option($socket, SOL_SOCKET, SO_RCVTIMEO, ['sec' => 2, 'usec' => 0]); // 构造MCPE无连接Ping包 $OFFLINE_MESSAGE_DATA_ID = pack('c*', 0x00, 0xFF, 0xFF, 0x00, 0xFE, 0xFE, 0xFE, 0xFE, 0xFD, 0xFD, 0xFD, 0xFD, 0x12, 0x34, 0x56, 0x78); $currentTime = time(); $pingPacket = pack('cQ', 0x01, $currentTime); // ID_UNCONNECTED_PING + 64位时间戳 $pingPacket .= $OFFLINE_MESSAGE_DATA_ID; $pingPacket .= pack('Q', 2); // 客户端GUID(随便填一个固定值就行) // 发送Ping包 $sent = @socket_sendto($socket, $pingPacket, strlen($pingPacket), 0, $this->address, $this->port); if (!$sent) { socket_close($socket); return false; } // 接收服务器响应 $buffer = ''; $from = ''; $port = 0; $bytesReceived = @socket_recvfrom($socket, $buffer, 1024, 0, $from, $port); if ($bytesReceived > 0) { // 验证响应是否为UNCONNECTED_PONG包 if (ord($buffer[0]) === 0x1C) { // ID_UNCONNECTED_PONG // 解析时间戳,确认和发送的一致(可选校验) $receivedTime = unpack('Q', substr($buffer, 1, 8))[1]; if ($receivedTime === $currentTime) { $udp = true; } } } socket_close($socket); return $udp; }
关键修复点说明
- 改用socket扩展:相比
fsockopen,PHP的socket扩展对UDP操作的控制更精准,能直接设置接收超时并处理数据包收发。 - 添加响应接收与校验:发送ping后等待服务器返回的pong包,校验包ID(0x1C)和时间戳,确保是目标服务器的有效响应。
- 正确处理超时:通过
SO_RCVTIMEO设置接收超时,避免无限等待。
内容的提问来源于stack exchange,提问作者Thrallix
相关产品推荐
相关产品推荐

