Explication détaillée de PHP implémentant le ping (socket brut) via le protocole ICMP

藏色散人
Libérer: 2023-04-09 20:38:02
avant
3017 Les gens l'ont consulté

Apprentissage recommandé : "Tutoriel vidéo PHP"

PHP implémente le ping (socket brut) via le protocole ICMP

Récemment, je souhaite implémenter une fonction pour détecter si l'hôte cible est en ligne. Je l'ai vérifié sur Baidu. La plupart d'entre eux utilisent une connexion ouverte sur un certain port pour déterminer si l'hôte cible est en ligne. Tels que le port système Windows 3389 (RDP) et le port système *nix 22 (SSH).

Mais cela posera un problème. Si l'hôte cible n'ouvre pas ces ports, cela entraînera des erreurs de jugement. Ce n’est pas parce qu’un port n’est pas ouvert que l’hôte cible est hors ligne.

Étant donné que la plupart des appareils répondent au ping, j'ai pensé à utiliser ping pour implémenter cette fonction. En interrogeant à nouveau Baidu, j'ai découvert que la plupart des didacticiels utilisent la fonction exec() pour appeler la commande système ping, ce qui est évidemment très dangereux.

J'ai donc finalement décidé d'utiliser le socket brut fourni par PHP et de créer moi-même le package ICMP pour implémenter le ping.

Pour créer un paquet ICMP, nous devons d'abord comprendre la structure du paquet ICMP.

Explication détaillée de PHP implémentant le ping (socket brut) via le protocole ICMP

Comme vous pouvez le constater, un paquet ICMP standard se compose d'un type de 8 bits, d'un code de 8 bits, d'une somme de contrôle de 16 bits, d'un identifiant de 16 bits et d'une séquence de 16 bits. nombre et données. Ensuite, nous créerons un tel package de données via 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);//数据
}
Copier après la connexion

Ensuite, calculez la somme de contrôle.

$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字节
Copier après la connexion

Somme de contrôle de remplissage dans le paquet.

$package[2] = $checksum[0];
$package[3] = $checksum[1];//填充校验和
Copier après la connexion

De cette manière, un paquet de données ICMP standard est construit et peut être envoyé directement à l'hôte cible. Prêt à partir ~

$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;//输出结果
Copier après la connexion

Appuyez pour exécuter et une demande de ping est terminée. Si rien d’autre, le résultat devrait être celui indiqué ci-dessous.

64 bytes from 192.168.1.1: icmp_seq=1  ttl=128 time=0.589ms
Copier après la connexion

Enfin, j'ai empaqueté ce code dans une fonction. Ajoutez-le à votre code et utilisez ping(string $host, int $retry) lorsque vous devez l'appeler.

<?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
}
?>
Copier après la connexion

S'il y a des erreurs ou un manque de détails dans l'article, veuillez le signaler et en discuter dans la zone de commentaires.

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Étiquettes associées:
php
source:joyrunc
Déclaration de ce site Web
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn
Tutoriels populaires
Plus>
Derniers téléchargements
Plus>
effets Web
Code source du site Web
Matériel du site Web
Modèle frontal