adawolfa/react-icmp-ping

Simple ICMP pinging library for ReactPHP.


Keywords
ping, icmp, reactphp
License
MIT

Documentation

Ping for ReactPHP

This is a simple library for ICMP echo (ping) queries.

Warning: For ICMP communication, you need to have sockets extension enabled. It doesn't work without it, hence the Composer platform requirement. And there's more. Default ReactPHP event loop implementation (stream_select()) doesn't support streams created by socket_create(), don't really know about others (and I want to be able to use the default one). Because of that, replies are collected using timers. Those timers are automatically cancelled when there's nothing to collect, but you should be aware of that. Read more about precision.

Also, on Linux, raw sockets require elevated privileges of your process. Apart from running it under a root (which is a stupid idea), you could assign CAP_NET_RAW capability to the PHP interpreter (I wouldn't do that), or yet better, use capsh for your process:

apt install libcap2-bin
capsh --caps="cap_net_raw+ep" -- -c "php app.php"

The most sane way to do it is adding the capability in your service unit file:

AmbientCapabilities = CAP_NET_RAW

This is useful library for things like checking a host existence or status, but I encourage you from using it for an actual response time measurement.

Usage

Send a single ping query to a host:

$loop = React\EventLoop\Factory::create();
$resolver = (new React\Dns\Resolver\Factory)->create('1.1.1.1', $loop);
$ping = new Adawolfa\ICMP\Ping($loop, $resolver);

$ping->ping('8.8.8.8', $timeout = 5.0)->then(
	
    // Received a reply within given timeout.
    function(float $time) {
        printf('Pong in %.4f!', $time);
    },
    
    // Host doesn't exist or it's dead.
    function(Exception $exception) {
        printf('Error!');
    }
    
);

$loop->run();

You can supply either an IPv4 address or a hostname (that's what the resolver is for).

In case you need to query several hosts simultaneously, just reuse the Ping class instance, it does take care of concurrent requests.

In order to query a host periodically, use the periodic() method:

$ping->periodic('localhost', function(float $time, Exception $exception = null) {
    printf('time: %.4f, error: %s', $time, $exception ?? 'none');
});

This will ping localhost for eternity. You can interrupt it:

$stop = $ping->periodic(...);

// Just invoke it later.
$stop();

Precision

By default, there's a 100 ms periodic timer checking for ICMP responses. This is OK for a reasonable use, like uptime check; it will resolve immediately for online hosts and it won't cause much of an overhead. You could even raise the time up to a second.

If a precision is important to you, it does make sense to lower the check interval. Because of how ReactPHP event loop works, there's no point in setting it to less than 1 ms. That's really the best you can get out of it.

$ping = new Adawolfa\ICMP\Ping($loop, $resolver, 1 / 1000);

It doesn't really matter how much hosts you ping simultaneously, there's still just a single timer. Under the hood, the library just switches the socket to non-blocking mode and calls socket_recvfrom() until it returns false (there's nothing to read).

This doesn't cause any unwanted event loop lagging, unlike socket_sendto() (which, however, isn't any slower than fwrite() and any stream writing function for that matter).

Once all the queries are resolved or timed out, underlying timer is automatically cancelled.