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
Welcome back! If you're the original bug submitter, here's where you can edit the bug or add additional notes.
If you forgot your password, you can retrieve your password here.
Password:
Status:
Package:
Bug Type:
Summary:
From: omarvillanueva at gmail dot com
New email:
PHP Version: OS:

 

 [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

Pull Requests

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-2024 The PHP Group
All rights reserved.
Last updated: Sun Oct 27 16:01:27 2024 UTC