首頁 > 後端開發 > php教程 > 詳解PHP透過ICMP協定實現ping(原始套接字)

詳解PHP透過ICMP協定實現ping(原始套接字)

藏色散人
發布: 2023-04-09 20:38:02
轉載
3063 人瀏覽過

推薦學習:《PHP影片教學

#PHP透過ICMP協定實作ping(原始套接字)

最近想實現一個檢測目標主機是否在線的功能,用百度查了查,多是使用打開到某個端口的連接來判斷目標主機是否在線的。如Windows系統3389埠(RDP)和*nix系統的22埠(SSH)。

但這樣會出現一個問題,目標主機如果沒有開放這些端口,則會導致判斷上的錯誤。某個連接埠不開放並不代表目標主機離線。

由於大多數裝置都會回應ping,因此想到了使用ping來實現這個功能。再次查詢百度,發現大多數教程都使用exec()函數呼叫系統ping指令來實現,這顯然很不安全。

所以最後決定使用PHP提供的原始套接字,自己建構ICMP套件來實現ping。

要建立一個ICMP包,首先我們要了解ICMP包的結構。

詳解PHP透過ICMP協定實現ping(原始套接字)

可以看到,一個標準的ICMP套件由8位元類型,8位元碼,16位元校驗和,16位元ID,16位元序號和資料組成。接下來,我們就透過PHP建構一個這樣的資料包。

$package = chr(8).chr(0);//模式 8 0
$package .= chr(0).chr(0);//置零校验和
$package .= "R"."C";//ID 这里是我随便填的
$package .= chr(0).chr(1);//序列号 一样 随便填的
for($i=strlen($package);$i<64;$i++){//填充满64位
    $package .= chr(0);//数据
}
登入後複製

接下來計算校驗和。

$tmp = unpack("n*",$package);//把数据16位一组放进数组里
$sum = array_sum($tmp);//求和
$sum = ($sum >> 16) + ($sum & 0xFFFF);//结果右移十六位 加上结果与0xFFFF做AND运算
$sum = $sum + ($sum >> 16);//结果加上结果右移十六位
$sum = ~ $sum;//做NOT运算
$checksum = pack("n*", $sum);//打包成2字节
登入後複製

把校驗和填入資料包。

$package[2] = $checksum[0];
$package[3] = $checksum[1];//填充校验和
登入後複製

這樣,一個標準的ICMP封包就建置好了,可以直接傳送給目標主機了。 Ready to go~

$host = "192.168.1.1";//设置目标主机
$socket=socket_create(AF_INET, SOCK_RAW, getprotobyname(&#39;icmp&#39;));//创建原始套接字
$start = microtime();//记录开始时间
socket_sendto($socket, $package, strlen($package), 0, $host, 0);//发送数据包
$read = array($socket);//初始化socket
$select = socket_select($read, $write, $except, 5);
if ($select === FALSE){
    $icmpError = "socket_select()方法发生错误,原因:".socket_strerror(socket_last_error());
    socket_close($socket);
}else if($select === 0){
    $icmpError = "请求超时";
    socket_close($socket);
}
if($icmpError !== NULL){
    echo $icmpError;
    exit();
}
socket_recvfrom($socket, $recv, 65535, 0, $host, $port);//接受回传数据
/*回传数据处理*/
$end = microtime();//记录结束时间
$recv = unpack("C*", $recv);
$length = count($recv) - 20;//包长度 减去20字节IP报头
$ttl = $recv[9];//ttl
$seq = $recv[28];//序列号
$duration = round(($end - $start) * 1000,3);//计算耗费的时间
echo "{$length} bytes from {$host}: icmp_seq={$seq}  ttl={$ttl} time={$duration}ms".PHP_EOL;//输出结果
登入後複製

輕敲運行,一次ping請求就完成了。不出意外的話,結果應該如下顯示。

64 bytes from 192.168.1.1: icmp_seq=1  ttl=128 time=0.589ms
登入後複製

最後,我將這些程式碼打包成了一個函數。把它加入你的程式碼裡,需要呼叫的時候,使用ping(string $host, int $retry)即可。

<?php
 function ping($host, $retry = 1){
    $g_icmp_error = NULL;
    $write = NULL;
    $except = NULL;//初始化所需变量
    $package = chr(8).chr(0);//模式 8 0
    $package .= chr(0).chr(0);//置零校验和
    $package .= "R"."C";//ID
    $package .= chr(0).chr(1);//序列号
    for($i=strlen($package);$i<64;$i++){
        $package .= chr(0);
    }
    $tmp = unpack("n*",$package);//把数据16位一组放进数组里
    $sum = array_sum($tmp);//求和
    $sum = ($sum >> 16) + ($sum & 0xFFFF);//结果右移十六位 加上结果与0xFFFF做AND运算
    $sum = $sum + ($sum >> 16);//结果加上结果右移十六位
    $sum = ~ $sum;//做NOT运算
    $checksum = pack("n*", $sum);//打包成2字节
    $package[2] = $checksum[0];
    $package[3] = $checksum[1];//填充校验和
    $socket=socket_create(AF_INET, SOCK_RAW, getprotobyname(&#39;icmp&#39;));//创建原始套接字
    $start = microtime();//记录开始时间
    socket_sendto($socket, $package, strlen($package), 0, $host, 0);//发送数据包
    $read = array($socket);//初始化socket
    $select = socket_select($read, $write, $except, 5);
    if ($select === FALSE){
        $icmpError = "socket_select()方法发生错误,原因:".socket_strerror(socket_last_error());
        socket_close($socket);
    }else if($select === 0){
        $icmpError = "请求超时";
        socket_close($socket);
    }
    if($icmpError !== NULL){
        echo $icmpError;
        exit();
    }
    socket_recvfrom($socket, $recv, 65535, 0, $host, $port);//接受回传数据
    /*回传数据处理*/
    $end = microtime();//记录结束时间
    $recv = unpack("C*", $recv);
    $length = count($recv) - 20;//包长度 减去20字节IP报头
    $ttl = $recv[9];//ttl
    $seq = $recv[28];//序列号
    $duration = round(($end - $start) * 1000,3);//计算耗费的时间
    echo "{$length} bytes from {$host}: icmp_seq={$seq}  ttl={$ttl} time={$duration}ms".PHP_EOL;//输出结果
    
    socket_close($socket);//关闭socket
}
?>
登入後複製

文中如果有錯誤或不詳細的地方,歡迎在評論區指出和討論。

以上是詳解PHP透過ICMP協定實現ping(原始套接字)的詳細內容。更多資訊請關注PHP中文網其他相關文章!

相關標籤:
php
來源:joyrunc
本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn
熱門教學
更多>
最新下載
更多>
網站特效
網站源碼
網站素材
前端模板