Files
sentinel/server_status_detector.rb
T

115 lines
3.7 KiB
Ruby

# <<*>> -^v- SNOWARE APPLICATION
# usage: MINECRAFT Server monitoring.
# author: S.A.
# time: 2026-06-07
# Copyright (c) [2026] [S.A. SNOWARE-SCU]
# [MineSentinel] is licensed under Mulan PubL v2.
# You can use this software according to the terms and conditions of the Mulan PubL v2.
# You may obtain a copy of Mulan PubL v2 at:
# http://license.coscl.org.cn/MulanPubL-2.0
# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
# EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
# MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
# See the Mulan PubL v2 for more details.
# frozen_string_literal: true
module ServerStatusDetector
require 'socket'
require 'timeout'
class TcpServer
# rubocop:disable Metrics/MethodLength, Metrics/AbcSize
# @param host [String]
# @param port [Integer]
# @param timeout [Integer]
# @return [Hash{Symbol=>Integer, Symbol=>Symbol, Symbol=>Float,nil}]
def self.test_port(host = '127.0.0.1', port = 25_565, timeout = 10)
result = {
port: port,
status: :unknown,
response_time: nil
}
begin_time = Time.now
# 带错误处理的代码需要使用 begin...end
begin
Timeout.timeout(timeout) do # 此方法接收整数和代码块作为参数,并在超时时引发错误
socket = TCPSocket.new(host, port)
result[:status] = :open
result[:response_time] = (Time.now - begin_time).round(4)
socket.close
end
rescue Timeout::Error
result[:status] = :timeout
rescue Errno::ECONNREFUSED
result[:status] = :closed
rescue Errno::ECONNRESET
result[:status] = :interrupted
rescue Errno::ENETUNREACH, Errno::EHOSTUNREACH
result[:status] = :unreachable
rescue StandardError => e
result[:status] = :error
result[:error] = e.message
end
result
end
# rubocop:enable Metrics/MethodLength, Metrics/AbcSize
end
# 继承为TcpServer编写的属性和方法
class MinecraftServer < TcpServer
# rubocop:disable Metrics/MethodLength
# @return [Hash{Symbol=>Boolean, Symbol=>Array<String>, Symbol=>String}]
def self_diag
res = test_port('127.0.0.1', 25_565)
unless res[:error].nil?
message = "Unexpected ERROR when testing SMP by TCP: #{res[:status]},INFO: #{res[:error]}"
puts message
return {
type: false,
operation: %w[Record],
message: message
}
# 立刻错误处理是好习惯
end
case res[:status]
when :open
MethodExecutor.record('debug', 'self_diag',
"TCP check OK — #{res[:response_time]}s")
{
type: true
}
when :timeout
MethodExecutor.record('warn', 'self_diag',
'TCP check timed out.')
{
type: false,
operation: %w[Record],
message: 'We sent packet without response.'
}
when :closed
MethodExecutor.record('warn', 'self_diag',
'TCP port closed — server is down.')
{
type: false,
operation: %w[Record ForceResetServerProcess], # 重启时注意要设置最大尝试次数
message: 'Server died for unknown reason, and will be restarted.'
}
else
MethodExecutor.record('warn', 'self_diag',
"TCP check returned unexpected status: #{res[:status]}.")
{
type: false,
operation: %w[Record],
message: "Unexpected status: #{res[:status]}."
}
end
end
# rubocop:enable Metrics/MethodLength
end
end