0

I am looking for which ajax call or jquery api to be receiving streaming data from php libevent client.

The Libevent Client will be receiving data from Libevent Server.c

Server.c

/* For socket functions */
#include <sys/socket.h>
#include <event2/event.h>
#include <event2/buffer.h>
#include <event2/bufferevent.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/un.h>
#include <event2/listener.h>

#include <arpa/inet.h>

#include <signal.h>

#define MAX_LINE 16384

void readcb(struct bufferevent *bev, void *ctx)
{
  /* This callback is invoked when there is data to read on bev. */
  struct evbuffer *input = bufferevent_get_input(bev);
  int len = evbuffer_get_length(input);
  char *data;
  data = malloc(len);
  evbuffer_copyout(input, data, len);
  free(data);
}

void writecb(struct bufferevent *bev, void *ctx)
{
  //here i will be checking my database and memcache new updates
  //it will wait randomly between 2 to 6 seconds to check again

  char *message = "Continuous message from your Server";
  evbuffer_add(bufferevent_get_output(bev), message, strlen(message));
  sleep(2+rand()%4);
}

void errorcb(struct bufferevent *bev, short error, void *ctx)
{
  if (error & BEV_EVENT_EOF) {
    /* connection has been closed, do any clean up here */
    /* ... */
  } else if (error & BEV_EVENT_ERROR) {
    /* check errno to see what error occurred */
    /* ... */
  } else if (error & BEV_EVENT_TIMEOUT) {
    /* must be a timeout event handle, handle it */
    /* ... */
  }
  bufferevent_free(bev);
}

void do_accept(evutil_socket_t listener, short event, void *arg)
{
  struct event_base *base = arg;
  struct sockaddr_storage ss;
  socklen_t slen = sizeof(ss);
  int fd = accept(listener, (struct sockaddr*)&ss, &slen);
  if (fd < 0) {
    perror("accept");
  } else if (fd > FD_SETSIZE) {
    close(fd);
  } else {
    struct bufferevent *bev;
    evutil_make_socket_nonblocking(fd);
    bev = bufferevent_socket_new(base, fd, BEV_OPT_CLOSE_ON_FREE);
    bufferevent_setcb(bev, readcb, writecb, errorcb, NULL);
    bufferevent_setwatermark(bev, EV_READ, 0, MAX_LINE);
    bufferevent_enable(bev, EV_READ|EV_WRITE);
  }
}

void run(void)
{
  evutil_socket_t listener;
  struct sockaddr_un sun;
  int len;
  struct event_base *base;
  struct event *listener_event;
  base = event_base_new();

  if (!base)
    return; /*XXXerr*/

  listener = socket(AF_UNIX, SOCK_STREAM, 0);
  evutil_make_socket_nonblocking(listener);

#ifndef WIN32
{
    int one = 1;
    setsockopt(listener, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one));
}
#endif

  memset(&sun, 0, sizeof(sun));

  sun.sun_family = AF_UNIX;
  strcpy(sun.sun_path, "/tmp/influenzaunix.sock");

  unlink(sun.sun_path);
  len = strlen(sun.sun_path) + sizeof(sun.sun_family);
  if (bind(listener, (struct sockaddr *)&sun, len) < 0) {
    perror("bind");
    return;
  }

  if (listen(listener, 16)<0) {
    perror("listen");
    return;
  }

  listener_event = event_new(base, listener, EV_READ|EV_PERSIST, do_accept, (void*)base);

  event_add(listener_event, NULL);

  event_base_dispatch(base);
}

int main(int c, char **v)
{
  setvbuf(stdout, NULL, _IONBF, 0);

  run();
  return 0;
}

Compiled with

gcc -o Server Server.c -levent

Then the Client.php is

<?php
define('MAX_LINE', '16384');
define('CHUNK_SIZE', '128');

class Server extends Thread{
    public function run(){
        passthru('./Server');
    }
}

class Client extends Thread{

    public function readcb($bev, $ctx)
    {
        $tmp = array();

        while (1) {
            $data = event_buffer_read($bev,CHUNK_SIZE);
            $size = strlen($data);
            if($size <= 0)
                break;
            $tmp[] = $data;
        }
        $data = implode($tmp);
        echo $data."\n"; //display the data received from server
        flush(); /*flush the data so that an ajax call will be receiving it*/
    }

    public function eventcb($bev, $events, $ptr)
    {
    }

    public function run()
    {
        $sockpath = '/tmp/influenzaunix.sock';
        $socket = stream_socket_client("unix://{$sockpath}",$errorNumber,$errorString,STREAM_CLIENT_CONNECT);
        stream_set_blocking($socket, 0); //non-blocking mode

        if (($socket) === FALSE){
            echo 'connect error';
        }
        else{
            $message = "Send data about this topic"; //subscribing to get data about a topic
            $bev = event_buffer_new($socket, array($this,"readcb"), NULL, array($this,"eventcb"));

            $base = event_base_new();

            if($base !== FALSE)
            {
                event_buffer_base_set($bev,$base);
                event_buffer_enable($bev, EV_READ|EV_WRITE);
                event_buffer_write($bev, $message, strlen($message));
            }

            event_base_loop($base);
        }
    }
}

$server = new Server();
$server->start();

sleep(3); /*let's wait for 3 seconds to make sure Server is ready to accept connection*/    

$client = new Client();
$client->start();
?>

The Client.php can only be run via CLI mode with RunClient.php below

<?php
passthru('php Client.php');
?>

I am having a challenge to come up with an ajax call or jquery ajax call to be receiving the flushed data from Client.php. The JQuery ajax or Ajax call cannot call Client.php directly instead it will call RunClient.php. Please I will greatly appreciate if someone can make an attempt to provide an ajax call or Jquery ajax call to be receiving the flushed data.

amachree tamunoemi
  • 817
  • 2
  • 16
  • 33
  • what happens when you call RunClient.php in a browser ? does it answer immediatly with a blank page or does the browser waits for something that never comes event if your server emits events ? – Jerome WAGNER Jul 29 '16 at 15:54
  • when run in a browser it waits for localhost until it times out and 504 Gateway Timeout appeared. Though I don't intend to run it in browser. It will only be called by an ajax call. – amachree tamunoemi Jul 29 '16 at 16:04

1 Answers1

0

Testing with a browser is a first step to understand what is happening.

I am not familiar with php Thread class, but what may be happening is that when you call

passthru('php Client.php');

in RunClient.php, the call is blocking, waiting for php Client.php to finish.

Since Client.php is a long running server (it basically never ends), you never get an answer even tough you used flush() inside Client.php.

You may want to try

<?php
$fp = fopen('php Client.php');
fpassthru($fp);
?>

to see if you see results in the browser (make sure your server emits data)

After that, you mention that you want to use ajax. You need to understand that an ajax call is a bit like a browser call : it is a request->response mechanism and not a streaming mechanism.

you can try maybe with a script tag in your html page

<script src='RunClient.php'/>

and modify your Client.php, instead of

echo $data."\n";

with

echo "document.write('<span>".$data."</span>');\n";

and simple datas without quotes. If it works, you will need to escape the data so that the emitted string is valid javascript.

Of course, this is a prototype. It all depends on what you want to do.

You may also want to use websockets for streaming. The protocol is made for streaming. cf https://developer.mozilla.org/en-US/docs/Web/API/WebSockets_API

Jerome WAGNER
  • 21,986
  • 8
  • 62
  • 77
  • Thanks. i tried everything but couldn't get the data. you are right that as long as Client.php is a long running server (it basically never ends) and it's impossible to get an answer even with the use of flush inside the Client.php. I actually want to provide a long polling server to update a web page with new data. Somehow don't want to use Websockets since it's not going to work on some browsers. Libevent or Socket.io would have been the best option. Please may you suggest the best hack with this solution I have here? – amachree tamunoemi Jul 30 '16 at 07:42