如何在 PHP 中通过特定端口 ping 服务器(fsockopen)

40 阅读2分钟

如何使用 PHP 中的 fsockopen 函数通过特定端口 ping 服务器?

function checkServer($domain, $port=80)
{
    global $checkTimeout, $testServer;
    $status = 0;

    $starttime = microtime(true);
    $file = @fsockopen ($domain, $port, $errno, $errstr, $checkTimeout);
    $stoptime = microtime(true);

    if($file)
    {
        fclose($file);
        $status = ($stoptime - $starttime) * 1000;
        $status = floor($status);
    }
    else
    {
        $testfile = @fsockopen ($testServer, 80, $errno, $errstr, $checkTimeout);
        if($testfile)
        {
            fclose($testfile);
            $status = -1;
        }
        else
        {
            $status = -2;
        }
    }

    return $status;
}

其中,$testServer 是一个外部服务器(如谷歌服务器),$checkTimeout 是超时时间。当目标服务器可通过端口访问时,checkServer 函数返回一个非零值;当无法通过端口访问时,函数返回零;当无法访问测试服务器时,函数返回 -2。

在实际应用中,该函数被用于检查服务器的可用性,例如,在监控系统中,当服务器无法访问时,监控系统会发出警告。

2、解决方案

对于上述问题,有以下几种可能的解决方案:

  1. 使用非阻塞 I/O:使用非阻塞 I/O 可以避免因为等待 I/O 操作而造成 CPU 占用过高的问题。在 PHP 中,可以使用 stream_set_blocking 函数来设置流为非阻塞模式。

  2. 使用多进程或多线程:使用多进程或多线程可以将任务并发执行,这样可以避免因为等待 I/O 操作而造成 CPU 占用过高的问题。在 PHP 中,可以使用 fork 函数来创建子进程,也可以使用 pthreads 扩展库来创建线程。

  3. 使用其他语言或工具:如果上述方法都不奏效,还可以考虑使用其他语言或工具来实现此功能。例如,可以使用 Python 中的 socket 库,或者使用 Bash 中的 nc 命令来 ping 服务器。

  4. 调整代码。在检查完服务器的可用性后,立即关闭打开的套接口。这样可以释放资源,并减少 CPU 占用。

以下代码演示了如何使用非阻塞 I/O 来实现 checkServer 函数:

function checkServer($domain, $port=80)
{
    global $checkTimeout;
    $status = 0;

    $starttime = microtime(true);
    $stream = stream_socket_client("tcp://{$domain}:{$port}", $errno, $errstr, $checkTimeout);
    if ($stream) {
        stream_set_blocking($stream, 0);
        $readable = stream_select([$stream], [], [], 0);
        if ($readable) {
            $status = ($stoptime - $starttime) * 1000;
            $status = floor($status);
        }
        fclose($stream);
    }

    return $status;
}
  1. 使用轮询机制。轮询机制可以避免因为等待 I/O 操作而造成 CPU 占用过高的问题。在 PHP 中,可以使用 pcntl_fork 函数来创建子进程,然后使用 pcntl_wait 函数来等待子进程完成。这样可以将任务并发执行,避免因为等待 I/O 操作而造成 CPU 占用过高的问题。

以下代码演示了如何使用轮询机制来实现 checkServer 函数:

function checkServer($domain, $port=80)
{
    global $checkTimeout;
    $status = 0;

    $pid = pcntl_fork();
    if ($pid == 0) {
        $starttime = microtime(true);
        $file = @fsockopen ($domain, $port, $errno, $errstr, $checkTimeout);
        $stoptime = microtime(true);

        if($file) {
            fclose($file);
            $status = ($stoptime - $starttime) * 1000;
            $status = floor($status);
        }
        exit($status);
    } else {
        pcntl_wait($status);
    }

    return $status;
}

3、代码例子

<?php
// 设置超时时间
$checkTimeout = 10;

// 设置测试服务器
$testServer = 'google.sk';

// 要检查的服务器和端口
$domain = 'www.example.com';
$port = 80;

// 调用 checkServer 函数检查服务器可用性
$status = checkServer($domain, $port);

// 根据检查结果输出信息
if ($status > 0) {
    echo "服务器 {$domain}{$port} 可用,响应时间为 {$status} 毫秒。";
} elseif ($status == -1) {
    echo "服务器 {$domain}{$port} 不可达,但测试服务器 {$testServer} 可达。";
} else {
    echo "服务器 {$domain}{$port} 和测试服务器 {$testServer} 都不可达。";
}