php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
Bug #62566 Problem with stream_socket_client and libevent
Submitted: 2012-07-14 17:08 UTC Modified: 2013-01-31 19:25 UTC
From: omarvillanueva at gmail dot com Assigned: tony2001 (profile)
Status: Not a bug Package: libevent (PECL)
PHP Version: 5.4Git-2012-07-14 (Git) OS: MacOSX
Private report: No CVE-ID: None
 [2012-07-14 17:08 UTC] omarvillanueva at gmail dot com
Description:
------------
I am trying to use a connection opened with stream_socket_client() on PHP, and 
handle the response from the remote server through a READ event with libevent. 
Sadly, there are no examples of this in the PHP documentation. The usual approach 
using while (!feof($fp)) { //something } or any other blocking approach is not 
good enough for me, because I'm writing something like a relay server, hence the 
need of libevent.

As you can see in the code snippet, it looks perfectly normal, but the 
onReceive() callback never gets called. If I change it for EV_WRITE|EV_PERSIST 
(or if I create an event for writes), it gets called, but obviously no data is 
received.

BTW, the snippet works great if I use stream_socket_server, but that's not what I 
want to do.

Test script:
---------------
$fp = stream_socket_client("tcp://some_host:some_port", $errno, $errstr);

stream_set_blocking($fp, 0);

$base = event_base_new();
$event_fd = event_new();
event_set($event_fd, $fp, EV_READ| EV_PERSIST, 'onReceive');
event_base_set($event_fd, $base);
event_add($event_fd);
event_base_loop($base);

Expected result:
----------------
I expect that the "onReceive" callback function gets called on every incoming 
data on a stream_socket_client connection.

Actual result:
--------------
The "onReceive" never gets called.

Patches

Add a Patch

Pull Requests

Add a Pull Request

History

AllCommentsChangesGit/SVN commitsRelated reports
 [2012-07-17 01:31 UTC] omarvillanueva at gmail dot com
-Status: Open +Status: Assigned
 [2012-07-17 01:31 UTC] omarvillanueva at gmail dot com
UPDATE: The provided snippet wasn't accurate enough, and actually there was no 
bug in it. What I tried to do was starting an event loop inside another triggered 
event, which failed. I managed to make it work by creating the new event inside a 
new fork with pcntl_fork(). I've read the libevent documentation and it says that 
a new base should be used, but when I call event_base_loop(), it blocks the 
previous one, and no new events are triggered in it until the new one ends. I'd 
like to know if there's any way to do it without pcntl_fork(). Am I missing 
something?

I've created a proof of concept here:

<?php

class Relay{

    private $connectionHandler;
    private $newHandler;
    
    public function test(){
        
        $this->connectionHandler = stream_socket_client("tcp://localhost:80", 
$errno, $errstr);

        if (!$this->connectionHandler) {
            echo "CRITICAL ERROR: $errstr ($errno)\n";
            exit;
        }

        stream_set_blocking($this->connectionHandler, 0);

        $this->eventBase = event_base_new();
        
        $buf = event_buffer_new($this->connectionHandler, array($this, 
'onReceive'), array($this, 'onWrite'), array($this, 'onError'), $this-
>eventBase);
        
        event_buffer_base_set($buf, $this->eventBase);
        event_buffer_priority_set($buf, 10);
        event_buffer_watermark_set($buf, EV_READ, 0, 0xFFFFFF);
        event_buffer_enable($buf, EV_READ | EV_PERSIST);
        
        event_base_loop($this->eventBase);
    }

    public function onReceive($buffer, $base) {

        $receiveBuffer = null;
        
        while ($rawData = event_buffer_read($buffer, 256)) {
            echo "RECEIVED DATA AT 1: ".$rawData;
            $receiveBuffer .= $rawData;
        }
        
        if (!$this->newHandler){
            
            echo "CREATING NEW CONNECTION...\n";

            $this->newHandler = stream_socket_client("tcp://localhost:999", 
$errno, $errstr);

            if (!$this->newHandler) {
                echo "CRITICAL ERROR: $errstr ($errno)\n";
                exit;
            }

            stream_set_blocking($this->newHandler, 0);

            if (-1 == ($pid = pcntl_fork())){
                echo "COULD NOT FORK";
                exit;
            }else if ($pid == 0){
                $this->createNewConnection($this->newHandler);
                exit;
            }else{ //parent process
		//do something
            }
            
        }
        
        fwrite($this->newHandler, $receiveBuffer);
        
    }
    
    public function onWrite($buffer, $args) {
        echo "WRITE AT 1\n";
    }
    
    public function onError($buffer, $base) {
        echo "ERROR AT 1\n";
    }
    
    public function onReceive2($buffer, $args){
        
        $receiveBuffer = null;
        
        while ($rawData = event_buffer_read($buffer, 256)) {
            echo "RECEIVED DATA AT 2: ".$rawData;
            $receiveBuffer .= $rawData;
        }
        
        fwrite($this->connectionHandler, $receiveBuffer);
        
    }
    
    public function onWrite2($buffer, $args) {
        echo "WRITE AT 2\n";
    }
    
    public function onError2($buffer, $args) {
        echo "ERROR AT 2\n";
    }
    
    private function createNewConnection($newHandler){
        
        $newBase = event_base_new();
        $buf = event_buffer_new($newHandler, array($this, 'onReceive2'), 
array($this, 'onWrite2'), array($this, 'onError2'), $newBase);
        event_buffer_base_set($buf, $newBase);
        event_buffer_priority_set($buf, 9);
        event_buffer_watermark_set($buf, EV_READ, 0, 0xFFFFFF);
        event_buffer_enable($buf, EV_READ | EV_PERSIST);
        event_base_loop($newBase);

    }
}

$relayTest = new Relay();
$relayTest->test();
 [2013-01-25 08:53 UTC] tony2001@php.net
-Assigned To: +Assigned To: tony2001
 [2013-01-31 19:22 UTC] tony2001@php.net
-Status: Assigned +Status: Not a bug
 [2013-01-31 19:22 UTC] tony2001@php.net
I'd suggest you to take a look at some existing application using PECL/libevent.
phpDaemon https://github.com/kakserpom/phpdaemon/ is one of them.
If you still have some problems and think it's some PECL/libevent issue, please provide 
a short and self-sufficient reproduce case.
 [2013-01-31 19:25 UTC] tony2001@php.net
Regarding the documentation:
yes, the examples are missing and that is because nobody provides them.
Are you willing to help? I'd be more than happy to add your examples to the doc.
 
PHP Copyright © 2001-2019 The PHP Group
All rights reserved.
Last updated: Fri Apr 26 16:01:24 2019 UTC