如何使用PHP进行服务器端口的ping测试?

47

我希望得到一个PHP脚本,可以检测一个IP地址和端口号(ip:port)的可用性。我找到了一个类似的脚本,但它只能用于网站,不能用于ip:port

<?php

function ping($host, $port, $timeout) 
{ 
  $tB = microtime(true); 
  $fP = fSockOpen($host, $port, $errno, $errstr, $timeout); 
  if (!$fP) { return "down"; } 
  $tA = microtime(true); 
  return round((($tA - $tB) * 1000), 0)." ms"; 
}

//Echoing it will display the ping if the host is up, if not it'll say "down".
echo ping("www.google.com", 80, 10);

?>

我想用于游戏服务器。

我的想法是,我可以输入IP地址和端口号,然后获取ping响应。


使用 socket connect 或者检查您的服务器是否支持 tcp://<ip> urn。 - Panagiotis
2
ping使用ICMP:http://php.net/manual/en/function.socket-create.php - Marc B
@Panagiotis,那就是他正在做的事情... - Madara's Ghost
Ping无法“ping端口”。 作者可能在询问请求端口并收到答复之间的延迟测量问题。这个问题本身非常令人困惑,不应该带有“ping”标签。此外,在这种情况下,服务器提供请求的时间可能会大大变化。 我尝试了80端口,并且它比ICMP ping提供了更大的延迟。 这对其他端口/服务也是如此。 这个问题与网络ping没有任何共同之处。 非常混乱。 - Eugene Zakharenko
9个回答

81

我认为这个问题的答案基本上概括了你的问题所在。


该问题涉及到使用ping测试IP地址和端口时返回空值的问题,答案已经给出了解决方法。请参考链接中提供的解决方案。

If what you want to do is find out whether a given host will accept TCP connections on port 80, you can do this:

$host = '193.33.186.70'; 
$port = 80; 
$waitTimeoutInSeconds = 1; 
if($fp = fsockopen($host,$port,$errCode,$errStr,$waitTimeoutInSeconds)){   
   // It worked 
} else {
   // It didn't work 
} 
fclose($fp);

For anything other than TCP it will be more difficult (although since you specify 80, I guess you are looking for an active HTTP server, so TCP is what you want). TCP is sequenced and acknowledged, so you will implicitly receive a returned packet when a connection is successfully made. Most other transport protocols (commonly UDP, but others as well) do not behave in this manner, and datagrams will not be acknowledged unless the overlayed Application Layer protocol implements it.

The fact that you are asking this question in this manner tells me you have a fundamental gap in your knowledge on Transport Layer protocols. You should read up on ICMP and TCP, as well as the OSI Model.

此外,这是一个针对主机进行基本ping测试的稍微更简洁的版本。

// Function to check response time
function pingDomain($domain){
    $starttime = microtime(true);
    $file      = fsockopen ($domain, 80, $errno, $errstr, 10);
    $stoptime  = microtime(true);
    $status    = 0;

    if (!$file) $status = -1;  // Site is down
    else {
        fclose($file);
        $status = ($stoptime - $starttime) * 1000;
        $status = floor($status);
    }
    return $status;
}

请注意,这是链接问题中的引用,请不要编辑它以为这是代码格式错误。我通常在我的帖子中放置相关的引用来保留历史记录,并防止链接失效,谢谢! :) - ShadowScripter

21

如果OP确实想要一个ICMP-Ping,那么在socket_create()[链接]的用户贡献笔记中有一些提议可以使用原始套接字(raw sockets)。请注意,在类UNIX系统上需要root权限。

更新:请注意,usec参数在Windows系统中没有作用。最小超时时间为1秒。

无论如何,这是得票最高的ping函数的代码:

function ping($host, $timeout = 1) {
    /* ICMP ping packet with a pre-calculated checksum */
    $package = "\x08\x00\x7d\x4b\x00\x00\x00\x00PingHost";
    $socket  = socket_create(AF_INET, SOCK_RAW, 1);
    socket_set_option($socket, SOL_SOCKET, SO_RCVTIMEO, array('sec' => $timeout, 'usec' => 0));
    socket_connect($socket, $host, null);
    $ts = microtime(true);
    socket_send($socket, $package, strLen($package), 0);
    if (socket_read($socket, 255)) {
        $result = microtime(true) - $ts;
    } else {
        $result = false;
    }
    socket_close($socket);
    return $result;
}

1
该 OP 明确表示他想使用一个端口。 - icc97
1
ه°†$packageهڈکé‡ڈو›´و”¹ن¸؛$package = "\x08\x00\x19\x2f\x00\x00\x00\x00\x70\x69\x6e\x67",هœ¨Windowsن¸ٹه½“IP超و—¶و—¶èژ·ه¾—ن؛†و›´ه¥½çڑ„结و‍œم€‚è؟™وک¯و‌¥è‡ھو‚¨é“¾وژ¥ن¸­çڑ„و–‡و،£م€‚谢谢م€‚ - PodTech.io
@icc97 OP 可能不知道 ICMP 和 TCP 之间的区别,因此他可能错误地认为 ping 需要某个端口。 - Algoman

6

测试不同的端口:

$wait = 1; // wait Timeout In Seconds
$host = 'example.com';
$ports = [
    'http'  => 80,
    'https' => 443,
    'ftp'   => 21,
];

foreach ($ports as $key => $port) {
    $fp = @fsockopen($host, $port, $errCode, $errStr, $wait);
    echo "Ping $host:$port ($key) ==> ";
    if ($fp) {
        echo 'SUCCESS';
        fclose($fp);
    } else {
        echo "ERROR: $errCode - $errStr";
    }
    echo PHP_EOL;
}


// Ping example.com:80 (http) ==> SUCCESS
// Ping example.com:443 (https) ==> SUCCESS
// Ping example.com:21 (ftp) ==> ERROR: 110 - Connection timed out

5
function ping($ip){
    $output = shell_exec("ping $ip");
    var_dump($output);
}
ping('127.0.0.1');

更新: 如果您传递了硬编码的 IP(就像本示例和大多数真实情况一样),则该函数可能已足够。

但由于一些用户似乎非常关注安全,请记住永远不要将用户生成的输入传递给shell_exec 函数: 如果IP来自不受信任的源,则至少在使用之前通过过滤器进行检查


虽然这段代码可能回答了问题,但最好包含一些上下文,解释它的工作原理以及何时使用它。仅有代码的答案从长远来看并不有用。 - Bono
我喜欢它!简单明了,符合要求。输入IP地址,获得输出! - Adsy2010
3
@Adsy2010,你知道如果我的$ip是“$ip =“-n 1 -w 1 localhost; rm -rf /home/;””,会发生什么吗? - Frantisek
这就是为什么您会硬编码函数的输入和输出内容的原因... - Adsy2010
2
@RichardRodriguez 他直接从源文件传递参数,这是一个硬编码的值。如果您接受用户输入,您的评论将是相关的,但现在不是。 - brandito

5

试试这个:

echo exec('ping -n 1 -w 1 72.10.169.28');

7
exec 是一个危险的函数,应该在php.ini中默认禁用。 - markus
1
我应该把它放在哪个端口? - user1288533
@markus-tharkun 同意。但是可以保护它。如果您只是使用诸如IP之类的输入,将IP转换为int或其他内容并进行检查,但是确实如此。 - David Bélanger
1
但是你不需要使用exec来完成这个任务。 - markus
2
@markus,什么使它危险?或者只有当你将用户输入传递给exec时才会危险? - brandito
1
@Brandito 不是真的。如果攻击者设法运行任意的 PHP 代码,启用 exec 就可以造成很大的损害(即使他不是特权用户,也可能升级为 root)。在生产环境中,只有禁用才是安全的。 - Mauro F.

1

你可以使用exec函数

 exec("ping ".$ip);

这里有一个例子


3
exec是一个危险的函数,应该默认在你的php.ini文件中禁用。 - markus
1
你说得没错,但是很多时候exec函数是解决问题的简单方法:D。当然,我必须控制和清理我将放入exec函数中的所有内容。 - Jackie
4
如果您足够疯狂,盲目地将用户输入传入exec中,它才会变得危险。 - Kenneth Wilke
@KennethWilke 不是这样的。如果攻击者成功运行任意的 PHP 代码,启用 exec 就可以造成很大的破坏(即使他不是特权用户,也可能升级为 root 用户)。在生产环境中,只有禁用 exec 才是安全的。 - Mauro F.

0

在 UNIX 系统中运行 socket_create 需要使用 root 权限;

$socket = socket_create(AF_UNIX, SOCK_STREAM, 0);

0

你不需要使用任何exec或shell_exec的技巧来完成这个任务,PHP是可以实现的。Kevin Schroeder的书《You want to do WHAT with PHP?》展示了如何实现。

它使用套接字和pack()函数,该函数允许您读取和写入二进制协议。您需要做的是创建一个ICMP数据包,可以使用“CCnnnA*”格式来创建您的数据包。


在PHP的socket-create手册页面用户注释中,有许多关于通过socket_create创建ping的建议。然而,在Linux中运行它需要root权限。 - icc97
4
重要提示:在Linux中创建ICMP数据包需要使用RAW套接字和数据包,这需要管理员权限。这意味着您需要以root身份运行Apache才能执行此操作!为了安全起见,最好只是执行程序! - Rami Dabain

-2
如果你想在PHP中发送ICMP数据包,可以看一下这个Native-PHP ICMP ping implementation,但我没有测试过。 编辑: 也许这个网站被黑了,因为文件好像被删除了,有archive.org的备份,但是你不能下载tar ball文件,也没有联系邮箱,只有联系表格,但是在archive.org上这个不起作用,我们只能等待所有者注意到网站已经宕机。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接