Add event - Ev::CLOSE for EvIo watcher

Issue #23 wontfix
Alex Sky created an issue

Now event CLOSE of the socket, fire as Ev::READ event, you can add a new Ev::CLOSE event, to appoint a separate handler?

Something like this

$loop->io($fd, Ev::CLOSE, function ($w) {...});

Comments (4)

  1. Ruslan Osmanov repo owner

    There is no such kind of event as "closed file descriptor(socket)" neither in libev, nor in the back ends supported by libev, AFAIK.

    Also, I don't know how to check if a socket is open or closed without actually accessing it(recv or write system calls, for example). You might think of feof() or stream_get_meta_data(), but these won't work with EvIo::$fd because the underlying logic is based on internal flags changed by performing PHP stream operations. So EvIo::$fd is not quite true PHP stream.

    Thus, you have to check if corresponding operation on socket succeeds in the Ev::READ callback. For example, a server based on sockets extension might look like the following:

    <?php
    $address = '127.0.0.1';
    $port = 10000;
    
    error_reporting(E_ALL);
    ob_implicit_flush();
    
    // Writing to a socket that has been closed by the other end will result in
    // the process receiving SIGPIPE signal. The default action for this signal is
    // to terminate process. Although it may not happen in PHP, we'll ignore this
    // signal explicitly.
    pcntl_signal(SIGPIPE, SIG_IGN);
    
    
    if (($sock = socket_create(AF_INET, SOCK_STREAM, SOL_TCP)) === false) {
        fprintf(STDERR, "socket_create failed: %s\n",
            socket_strerror(socket_last_error()) );
        exit(1);
    }
    
    if (socket_bind($sock, $address, $port) === false) {
        fprintf(STDERR, "socket_bind failed: %s\n",
            socket_strerror(socket_last_error()) );
        exit(1);
    }
    
    if (socket_listen($sock, 5) === false) {
        fprintf(STDERR, "socket_listen failed: %s\n",
            socket_strerror(socket_last_error()) );
        exit(1);
    }
    
    do {
        if (($msgsock = socket_accept($sock)) === false) {
            fprintf(STDERR, "socket_accept failed: %s\n",
                socket_strerror(socket_last_error()) );
            break;
        }
    
        // Set O_NONBLOCK flag
        socket_set_nonblock($msgsock);
    
        $msg = "Welcome to the test server. To quit tpe 'quit'.\n";
        socket_write($msgsock, $msg, strlen($msg));
    
        $read_watcher = new EvIo($msgsock, Ev::READ,
            function ($watcher, $events) use ($msgsock)
        {
            if ($events & Ev::ERROR) {
                $watcher->stop();
                Ev::stop(Ev::BREAK_ALL);
                return;
            }
    
            if (false === ($buf = socket_read($msgsock, 2048, PHP_NORMAL_READ))) {
                fprintf(STDERR, "socket_read failed: %s\n",
                    socket_strerror(socket_last_error()));
                return;
            }
    
            $buf = trim($buf);
    
            if ($buf == 'quit') {
                $watcher->stop();
                socket_close($msgsock);
                return;
            }
    
            $talkback = "You said: $buf\n";
            if (! socket_write($msgsock, $talkback, strlen($talkback))) {
                $errno = socket_last_error();
                fprintf(STDERR, "socket_write failed: %s\n", socket_strerror($errno));
    
                $watcher->stop();
                socket_close($msgsock);
                echo "Connection closed\n";
    
                return;
            }
            echo "$buf\n";
        });
    
        echo "Running Ev loop\n";
    
        Ev::run();
    
        if (is_resource($msgsock)) {
            socket_close($msgsock);
        }
    } while (true);
    
    socket_close($sock);
    
  2. Log in to comment