ajax 调用从 php libevent 客户端获取连续/流式数据

Posted

技术标签:

【中文标题】ajax 调用从 php libevent 客户端获取连续/流式数据【英文标题】:ajax call to get continuous/streaming data from php libevent client 【发布时间】:2016-07-29 15:18:24 【问题描述】:

我正在寻找从 php libevent 客户端接收流数据的 ajax 调用或 jquery api。

Libevent 客户端将从 Libevent Server.c 接收数据

服务器.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;

编译

gcc -o Server Server.c -levent

那么Client.php就是

<?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();
?>

Client.php 只能通过 CLI 模式运行,下面是 RunClient.php

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

我有一个挑战来提出一个 ajax 调用或 jquery ajax 调用来接收来自 Client.php 的刷新数据。 JQuery ajax 或Ajax 调用不能直接调用Client.php,而是调用RunClient.php。如果有人可以尝试提供 ajax 调用或 Jquery ajax 调用来接收刷新的数据,我将不胜感激。

【问题讨论】:

在浏览器中调用 RunClient.php 会发生什么?如果您的服务器发出事件,它会立即以空白页面回答还是浏览器等待永远不会发生的事件? 在浏览器中运行时,它会等待 localhost 直到超时并出现 504 Gateway Timeout。虽然我不打算在浏览器中运行它。它只会被 ajax 调用调用。 【参考方案1】:

使用浏览器进行测试是了解发生了什么的第一步。

我不熟悉 php Thread 类,但可能发生的是当你调用时

passthru('php Client.php');

在 RunClient.php 中,调用被阻塞,等待php Client.php 完成。

由于Client.php 是一个长时间运行的服务器(它基本上永远不会结束),即使你在Client.php 中使用flush() 也很难得到答案。

你可以试试

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

查看您是否在浏览器中看到结果(确保您的服务器发出数据)

之后,您提到要使用 ajax。您需要了解 ajax 调用有点像浏览器调用:它是请求->响应机制而不是流机制。

您可以尝试在您的 html 页面中使用脚本标签

<script src='RunClient.php'/>

并修改您的 Client.php,而不是

echo $data."\n";

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

和不带引号的简单数据。如果它有效,您将需要转义数据,以便发出的字符串是有效的 javascript

当然,这是一个原型。这一切都取决于你想做什么。

您可能还想使用 websocket 进行流式传输。该协议是为流式传输而设计的。 cfhttps://developer.mozilla.org/en-US/docs/Web/API/WebSockets_API

【讨论】:

谢谢。我尝试了一切,但无法获取数据。你是对的,只要 Client.php 是一个长时间运行的服务器(它基本上永远不会结束),即使在 Client.php 中使用 flush 也不可能得到答案。我实际上想提供一个长轮询服务器来用新数据更新网页。不知何故不想使用 Websockets,因为它不适用于某些浏览器。 Libevent 或 Socket.io 将是最好的选择。请您建议我在这里使用此解决方案的最佳技巧?

以上是关于ajax 调用从 php libevent 客户端获取连续/流式数据的主要内容,如果未能解决你的问题,请参考以下文章

php中ajax调用出错的问题

PHP 从 Ajax 调用接收空的 $_POST 变量

Ajax+PHP打造等待进度条效果

通过ajax调用从php文件中接收多个值

使用 xhprof 分析 PHP 代码时,如何防止它破坏来自 JavaScript 客户端的 Ajax 调用?

PHP $connection->query($sql) 在 AJAX 调用期间从 PHP 脚本调用时返回 FALSE? [复制]