【问题标题】:Status checker for hundreds IP addresses数百个 IP 地址的状态检查器
【发布时间】:2013-05-09 12:45:25
【问题描述】:

我想知道如何制作一个状态检查器,在几分钟内检查大约 500 个地址? (如果它正在侦听,它会检查某个端口)。

但我关心性能......所以我不知道它是否可以用 PHP 完成。期待你们的建议。

如果你认为最好的方法是 PHP 或 C#,请给我一些例子。

办公室。我指的是 TCP 连接,而不是 http,因为我需要检查开放端口,例如:11740

编辑:

为这个问题添加了第三个赏金!请发布一些比已经发布的更好的答案。

【问题讨论】:

  • 这里很重要 - 这是基于 TCP 套接字还是基于数据报,例如 UDP?
  • TCP,fe。我想检查端口 80 上是否存在 IP 地址。
  • 您的性能瓶颈将是网络请求,而不是您用于检查的语言。

标签: php status


【解决方案1】:

这在 PHP 中非常可行,您可以在几秒钟内检查 500 个 IP。使用 mutli-curl 一次发送多个请求(即 100 个或全部 500 个)。响应时间与最慢的 IP 一样长。但是您可能希望设置一个合理的 curl 连接超时时间为几秒钟。我记得默认网络超时是 2 分钟。 http://php.net/manual/en/function.curl-multi-exec.php

请注意,我并不是说 PHP 是此类事情的最佳选择。但它可以在 PHP 中相当快速和轻松地完成。

这是一个完整的代码示例。我用真实的 URL 对其进行了测试,一切正常。 $results 数组将包含几乎所有可以从 curl 请求中获得的统计信息。在您的情况下,由于您只关心端口是否“打开”,因此您可以通过将 CURLOPT_NOBODY 设置为 true 来执行 HEAD 请求。

$urls    = array(
    'http://url1.com'=>'8080',
    'http://url2'=>'80',
    );
$mh        = curl_multi_init();
$url_count    = count($urls);
$i        = 0;
foreach ($urls as $url=>$port) {
    $ch[$i] = curl_init();
    curl_setopt($ch[$i], CURLOPT_RETURNTRANSFER, true);
    curl_setopt($ch[$i], CURLOPT_NOBODY, true);
    curl_setopt($ch[$i], CURLOPT_URL, $url);
    curl_setopt($ch[$i], CURLOPT_PORT, $port);
    curl_multi_add_handle($mh, $ch[$i]);
    $i++;
}
$running    = null;
do {
    $status    = curl_multi_exec($mh,$running);
    $info     = curl_multi_info_read($mh);
} while ($status == CURLM_CALL_MULTI_PERFORM || $running);

$results= array();
for($i = 0; $i < $url_count; $i++) {
    $results[$i]     = curl_getinfo($ch[$i]);
    // append port number request to URL
    $results[$i]['url']    .= ':'.$urls[$results[$i]['url']];
    if ($results[$i]['http_code']==200) {
        echo $results[$i]['url']. " is ok\n";
    } else {
        echo $results[$i]['url']. " is not ok\n";
    }
    curl_multi_remove_handle($mh, $ch[$i]);
}

curl_multi_close($mh);
print_r($results);

【讨论】:

  • 也不错,我总是忘记 curl_multi 函数!
  • 这完全不正确。例如,curl 有许多 FTP 特定选项 (CURLOPT_FTPSSLAUTH)。 CURLOPT_PROTOCOLS 选项单独列出 LDAP、SCP、TELNET、DICT、FILE。
  • @BrentBaisley 很抱歉这么长时间的回复延迟。你能告诉我一个例子来检查阵列中的地址和端口的状态吗?应该是这样的:$data = array('ip_address' =&gt; 'port');,谢谢!
  • 已经有了,查看第一行代码:$urls = array( 'http://url1.com'=&gt;'8080', 'http://url2'=&gt;'80', );
【解决方案2】:

您最好的选择可能是使用专用端口扫描器,例如 nmap。我不知道我脑海中的命令行选项,但应该可以输出结果文件并从 PHP 中解析它。

【讨论】:

    【解决方案3】:

    这是一种在 PHP 中使用 sockets 扩展名快速完成此操作的方法,方法是将所有套接字设置为非阻塞。从纯粹的网络角度来看,这是最有效的方式,因为这只是测试 TCP 连接性,并不交换任何实际数据。在 PHP/5.2.17-Win32 上测试。

    <?php
    
      // An array of hosts to check
      $addresses = array(
        '192.168.40.40',
        '192.168.5.150',
        '192.168.5.152',
        'google.com',
        '192.168.5.155',
        '192.168.5.20'
      );
      // The TCP port to test
      $testport = 80;
      // The length of time in seconds to allow host to respond
      $waitTimeout = 5;
    
      // Loop addresses and create a socket for each
      $socks = array();
      foreach ($addresses as $address) {
        if (!$sock = socket_create(AF_INET, SOCK_STREAM, SOL_TCP)) {
          echo "Could not create socket for $address\n";
          continue;
        } else echo "Created socket for $address\n";
        socket_set_nonblock($sock);
        // Suppress the error here, it will always throw a warning because the
        // socket is in non blocking mode
        @socket_connect($sock, $address, $testport);
        $socks[$address] = $sock;
      }
    
      // Sleep to allow sockets to respond
      // In theory you could just pass $waitTimeout to socket_select() but this can
      // be buggy with non blocking sockets
      sleep($waitTimeout);
    
      // Check the sockets that have connected
      $w = $socks;
      $r = $e = NULL;
      $count = socket_select($r, $w, $e, 0);
      echo "$count sockets connected successfully\n";
    
      // Loop connected sockets and retrieve the addresses that connected
      foreach ($w as $sock) {
        $address = array_search($sock, $socks);
        echo "$address connected successfully\n";
        @socket_close($sock);
      }
    
    /* Output something like:
    
    Created socket for 192.168.40.40
    Created socket for 192.168.5.150
    Created socket for 192.168.5.152
    Created socket for google.com
    Created socket for 192.168.5.155
    Created socket for 192.168.5.20
    4 sockets connected successfully
    192.168.40.40 connected successfully
    192.168.5.150 connected successfully
    google.com connected successfully
    192.168.5.20 connected successfully
    
    */
    

    【讨论】:

    • 抱歉延迟回答,但它没有按预期工作:当我添加 fe. i-does-not-exist.com.uk 脚本返回:1 sockets connected successfully i-does-not-exist.com.uk connected successfully,不管port我使用什么 - 1 或 99999 ,它仍然返回套接字已连接...
    • @Cyclone 端口 99999 不存在 - 最大有效 TCP 端口为 65535 - 端口号在 IPv4 中由 16 位整数表示。我只是在我的本地网络上简单地测试了这段代码,发现它按预期工作,我会做更多的尝试,看看在互联网上工作时是否可以重复/纠正你的问题。
    • @DaveRandom 你能试着修复你的代码吗?它似乎无法正常工作,即使端口关闭,代码总是返回成功连接,非常感谢您的帮助。请尝试一下,这对我很重要,也许对其他人也很重要。谢谢。
    • @DaveRandom 你能检查一下this Q吗?这是关于你的答案和+100赏金在那里!谢谢
    【解决方案4】:

    如其他答案所述,最好的方法是nmap

    你会想这样运行它(-PN 是不 ping,-sP 表示跳过端口扫描,只检查主机是否启动,-PS80 表示检查端口 80,@ 987654325@不做反向DNS查找,-oG -是机器可读格式输出,其他参数是IP地址或主机名):

    nmap -PN -sP -PS80 -n -oG - --send-ip IP1 IP2 ...
    

    它看起来像这样:

    $ nmap -n -PN -sP -PS80 -oG -  209.85.147.104 87.248.122.122 4.4.4.4
    # Nmap 5.21 scan initiated Tue Feb 21 01:07:20 2012 as: nmap -n -PN -sP -PS80 -oG - 209.85.147.104 87.248.122.122 4.4.4.4 
    Host: 209.85.147.104 () Status: Up
    Host: 87.248.122.122 () Status: Up
    Host: 4.4.4.4 ()    Status: Down
    # Nmap done at Tue Feb 21 01:07:21 2012 -- 3 IP addresses (2 hosts up) scanned in 0.95 seconds
    

    您可以毫无问题地从 PHP 运行和解析它。我在 PHP 方面不是很有经验,也没有测试过,但这里有一些示例代码:

    <?php
    $output = shell_exec('nmap -n -PN -sP -PS80 -oG - --send-ip ' . implode(" ", $ips));
    $result = array();
    foreach(preg_split("/\r?\n/", $output) as $line) {
        if (!(substr($line, 0, 1) === "#")) {
            $info = preg_split("[\t ]", $line);
            $result[$info[2]] = ($info[5] === "Up");
        }
    }
    ?>
    

    请注意,编写 PHP 代码或 C# 代码或任何其他代码都不是什么大不了的事,只是 nmap 非常擅长它所做的事情,而且用途广泛,编写这样的代码是重新发明轮子。如果你决定走这条路,请确保你的代码是异步的,否则一个缓慢的服务器会减慢你的整个扫描速度。

    【讨论】:

    • O_o ,很好,而且速度也很快!但它有一个问题:我猜-PS8080 是一个端口号 - 如果是这样,那么它不能正常工作,因为无论我设置什么端口,它都会显示主机已启动。
    • @Cyclone:你本地局域网上的服务器是偶然的吗?如果是这样,那么 nmap 默认使用 ARP 来检查主机是否启动,因为它更快。您需要使用--send-ip 告诉它始终尝试连接。我将编辑我的答案以包含此内容。
    • 不,我正在创建一个游戏服务器列表,并想检查每个服务器是否在线。我的测试模式:1021 IP addresses (248 hosts up) scanned in 39.95 seconds,性能也不错,我觉得40秒对于这样的要求还不算多?
    • 我已经使用了你编辑的shell命令:nmap -n -PN -sP -PS12345 -oG --send-ip xxx.xxx.xxx.xxx,请注意我已经将端口设置为:12345,它不在主机上,但它仍然显示它的Up .
    • @Cyclone:端口已关闭,但 host 已启动。意思是,它用RST 回复了连接请求。试试nmap -p 12345 -PS12345 -n -oG - --send-ip xxx.xxx.xxx.xxx 之类的东西,看看会告诉你什么=)
    【解决方案5】:

    正如 kz26 所述,命令行中的 nmap 将是您的最佳选择。使用 system、exec、shell_exec 等 PHP 函数来捕获处理结果。

    本指南应该可以帮助您入门http://www.cyberciti.biz/tips/linux-scanning-network-for-open-ports.html

    【讨论】:

      【解决方案6】:

      C# 将是一个更好的选择,因为您可以使用线程来加快进程。

      在 PHP 中,您基本上可以使用fsockopen() 来检查 TCP 端口是否打开。

      $host = 'example.com';
      $port = 80;
      $timeout = 30;
      
      $fp = @fsockopen($host, $port, $errno, $errstr, $timeout);
      if (!$fp) {
          echo "Port $port appears to be closed on $host  Reason: $errno: $errstr.\n";
      } else {
          echo "Port $port is open on $host\n";
          fclose($fp);
      }
      

      正如 kz26 所说,您还可以获得类似 nmap 的东西来检查一堆主机上的端口,并使用 php 调用它并处理结果。

      【讨论】:

      • 为了实现这一点,您需要有一个多核处理器。
      • 即使在单核处理器上,创建多个线程也会加快进程,因为您可以同时启动多个连接,而不必等待一个连接完成后再检查下一个。
      • 你是对的,我很抱歉我的错误。我不知道它是怎么发生的,但我忘了问题是关于网络的。
      • drew010:通常的方法是使用异步 API,而不是线程!反正nmap已经包含了相应的逻辑,所以……
      • 我用这个脚本创建了一个应用程序,稍加修改就可以很好地工作。 portscanner.standingtech.com
      【解决方案7】:

      PHP 应该能够在很短的时间内检查 500 个 ip,但这实际上取决于您的互联网连接和其他规格。其他用低级语言编写的工具会快一点,但我认为 php 对这项工作有好处。您必须在尝试之前将 php 执行限制设置为合适的值

      【讨论】:

        【解决方案8】:

        这里的关键字是非阻塞的。当尝试成功时,您会希望有一个特定的回调,同时能够运行更多的调用。

        DaveRandom 回答中的套接字等一些函数允许在常规 php 安装中使用非阻塞模式来执行此操作。您还可以使用不同的编程语言(例如 node.js)非常有效地执行此操作,因为它们被设计为非阻塞的。

        【讨论】:

          【解决方案9】:

          到目前为止,最有效的方法是使用原始套接字,但这需要管理权限和一些样板代码。您选择一个本地端口作为源,向每个目标地址的 80 端口发送一个 SYN 数据包,并收集源端口上的返回数据包。如果它们有 ACK 标志,则该端口对连接开放。如果他们有RST或者他们没有返回,端口没有打开/主机已经死了。

          显然,您需要手动等待您选择的任何“超时”,因为此方法会绕过操作系统网络堆栈。不过,这也需要您手动构建 TCP 和 IP 标头,我怀疑它可以从 PHP 中完成。它当然可以在 C 中完成,而且我相信 C# 也支持原始套接字(我自己还没有在 C# 中做过)。

          这是非常快,不会用一堆打开的连接或线程污染系统。

          【讨论】:

          • 不错的解决方案,没有任何例子对我来说是不可能的:/.
          【解决方案10】:

          您可以做的最好的事情是启动多个线程(您决定数量;可能多于 10 且少于 100)。在不同的线程上进行扫描将加快您的检查速度。我不确定您是否可以在 php 中执行多线程。如果没有,那么c#会更好。

          【讨论】:

            猜你喜欢
            • 2012-08-03
            • 2013-08-21
            • 1970-01-01
            • 1970-01-01
            • 2013-07-29
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2022-03-03
            相关资源
            最近更新 更多