Подтвердить что ты не робот

Перенос данных в браузер пользователя с сервера

Я хочу передать данные с сервера на браузер. Я уже знаю о php-функции ob_flush(), которая отправляет выходной буфер. Мне нужна помощь с некоторой логикой. Я использую API в реальном времени Facebook, поэтому я хочу каждый раз передавать данные пользователю каждый раз, когда Facebook посещает мой сайт.

Вот мой код, который я пытаюсь переместить данные в браузер, но не работает.

<?php
header('Access-Control-Allow-Origin: *');
header('Content-Type: text/event-stream');
ini_set("log_errors", 1);
ini_set("error_log", "php-error.log");
error_log( "LOGS STARTS FROM HERE" );
if(isset($_GET['hub_challenge'])){
    echo $_GET['hub_challenge'];    
}
if($_SERVER['REQUEST_METHOD'] == "POST"){
    $updates = json_decode(file_get_contents("php://input"), true); 
    // Replace with your own code here to handle the update 
    // Note the request must complete within 15 seconds.
    // Otherwise Facebook server will consider it a timeout and 
    // resend the push notification again.
    print_r($updates);
    ob_flush();
    flush();
    //file_put_contents('fb.log', print_r($updates,true), FILE_APPEND);     
    //error_log('updates = ' . print_r($updates, true));              
}
?>
4b9b3361

Ответ 1

Как показано в @som, вы можете просто использовать запросы с интервалом между ними, вам не нужно использовать сокеты.

Но дело в том, что вы пытаетесь получить данные из API и сразу же передаете его в браузер. Лучше всего отделить эти два шага.

В script, который получает данные из Facebook, сохраняет эти данные в базе данных или где-то еще:

if($_SERVER['REQUEST_METHOD'] == "POST"){
    $updates = json_decode(file_get_contents("php://input"), true); 

    insertDataToDatabase($updates); // you'll have to implement this.
}

Затем настройте страницу мониторинга:

monitor.php

<script>
lastId = 0;

$(document).ready(function() {
    getNewDataAtInterval();
});

function getNewDataAtInterval() {
    $.ajax({
        dataType: "json",
        url: "getData.php",
        data: {lastId: lastId}
    }).done(function(data) {
        for(i=0; i<data.messages.length; i++) {
            $("#messages").append("<p>" + data.messages[i]['id'] + ": " + data.messages[i]['message'] + "</p>");
            if (data.messages[i]['id'] > lastId) lastId = data.messages[i]['id'];
        }

        setTimeout(getNewDataAtInterval, 10000);
    }).fail(function( jqXHR, textStatus ) {
        alert( "Request failed: " + jqXHR.responseText );
    });
}
</script>

<div id="messages"></div>

Наконец, создайте серверную часть script, чтобы вернуть JSON с новыми сообщениями, загруженными из базы данных.

getData.php

$lastId = $_GET['lastId'];
$newMessages = getUpdatesFromDatabase($lastId);

exit(json_encode(array("messages"=>$newMessages)));

function getUpdatesFromDatabase($lastId) {
    // I'm using this array just as an example, so you can see it working.
    $myData = array(
        array("id"=>1,"message"=>"Hi"),
        array("id"=>2,"message"=>"Hello"),
        array("id"=>3,"message"=>"How are you?"),
        array("id"=>4,"message"=>"I'm fine, thanks")
    );

    $newMessages = array();
    foreach($myData as $item) {
        if ($item["id"] > $lastId) {
            $newMessages[] = $item;
            $newLastId = $item["id"];
        }
    }

    return $newMessages;
}

Ответ 2

Использование кометы или прототипа будет лучшей практикой здесь. Ajax увеличит нагрузку на сервер. Он будет часто опросить сервер. Вот некоторая необходимая информация об использовании кометы.

Вот пример того, как реализовать комету с php для отправки данных в реальном времени.

Ответ 3

У меня есть Pusher и Redis для передачи данных с сервера на браузер, в то время как я искал лучшее решение.

Толкатель

Вы можете удобно использовать трансляции событий с использованием драйвера Pusher с помощью SDK Pusher JavaScript.

this.pusher = new Pusher('pusher-key');

this.pusherChannel = this.pusher.subscribe('reference_id');

this.pusherChannel.bind('SomeEvent', function(message) {
    console.log(message.user);
});

Redis

Если вы используете вещатель Redis, вам нужно будет написать собственный пользователь Redis pub/sub, чтобы получать сообщения и транслировать их с использованием технологии websocket по вашему выбору. Например, вы можете использовать популярную библиотеку Socket.io, которая написана в Node.

Используя библиотеки socket.io и ioredis Node, вы можете быстро написать трансляцию событий для публикации всех событий, которые транслируются вашим приложением:

var app = require('http').createServer(handler);
var io = require('socket.io')(app);

var Redis = require('ioredis');
var redis = new Redis();

app.listen(6001, function() {
    console.log('Server is running!');
});

function handler(req, res) {
    res.writeHead(200);
    res.end('');
}

io.on('connection', function(socket) {
    //
});

redis.psubscribe('*', function(err, count) {
    //
});

redis.on('pmessage', function(method, channel, message) {
    message = JSON.parse(message);
    io.emit(channel + ':' + message.event, message.data);
});

Ответ 4

С обычным протоколом HTTP веб-браузеры всегда запрашивают ваш сервер для ответа. После отправки ответа сервер закрывает соединение.

Истинная технология постоянных соединений, такая как WebSocket, является одним из исключений, когда браузер делает определенный вид соединения с сервером и, понимая намерение, сервер будет поддерживать открытое соединение. Таким образом, как только данные будут "нажаты" в браузере, соединение всегда будет готово. Используя этот подход, нет необходимости временно сохранять данные, так как вы просто "передадите их".

Методы опроса (включая длительный опрос, когда сервер продолжает посылать "сердцебиение" клиенту, как если бы незначительный ответ медленно стекался) можно использовать в качестве рабочего процесса, но всегда есть некоторый интервал времени, когда соединение больше не открыта, пока не произойдет следующий цикл. Когда соединение отсутствует, ваш единственный вариант - временно сохранить данные, поэтому, когда ваш браузер вернется, вы можете нажать ожидающие данные. Если вы планируете хранить его во временной переменной, учтите, что с PHP script после завершения выполнения все данные, выделенные в памяти, связанной с этой областью, собираются в мусор.

Ответ 5

Зачем нужно толкать? Может быть, мне не хватает подсказки, но в остальном это единственный способ решить эту проблему? Вы хотите, чтобы у вас был возможность задать текст let say div, который мы называем statusUpdate, который показывает новые статусы из facebook, когда они публикуются? Затем вы можете:

Разделите процесс на поток коллекции статусов, который работает как демон, который постоянно пытается извлечь из FB API (не знаю никаких спецификаций или не имеет каких-либо знаний о FB API, но я могу только представить, что есть призывы к поиск, если есть новые статусы).

Не имеет значения, работает ли API потоковой передачи или что нам нужно подключать каждую X-вторую, что мы можем принять во внимание? Я бы установил демона в php, а затем запустил его с помощью SSH с помощью команды nohup php daemon.php, чтобы запустить script с бесконечным циклом, подобным этому:

Define('SLEEP', 1);  // loop every second

while (true) {  

   // do your thing, dont exit

   if( $fbMonkey->checkNewStatus() ){
        $fbMonkey->toDatabase(new_statuses);
  }

   if( some_reason_to_exit() == TRUE ){
      exit;
   }

    sleep(SLEEP);  
}
// While ends with break

Затем, возможно, включите в HTML целевого пользователя (конец браузера в браузере) функцию JavaScript, которая читает из таблицы с состояниями, которые заполняет демона, а затем для тех, которые не были помечены как просмотренные (пользователями или чем-то похожими ) и вернуть непрочитанные состояния в браузер. Если мы создадим бесконечный цикл в браузере для него и дадим ему обновить div statusUpdate и новое содержимое (html или tekst не имеет значения?). Я бы позвонил, как это задерживается, и проверяет каждые 20 секунд или около того и обновляет div.

http://www.w3schools.com/ajax/tryit.asp?filename=tryajax_xml2

function loadXMLDoc(url)
{
var xmlhttp;
var txt,xx,x,i;
if (window.XMLHttpRequest)
  {// code for IE7+, Firefox, Chrome, Opera, Safari
  xmlhttp=new XMLHttpRequest();
  }
else
  {// code for IE6, IE5
  xmlhttp=new ActiveXObject("Microsoft.XMLHTTP");
  }
xmlhttp.onreadystatechange=function()
  {
  if (xmlhttp.readyState==4 && xmlhttp.status==200)
    {
    txt="<table border='1'><tr><th>Title</th><th>Artist</th></tr>";
    x=xmlhttp.responseXML.documentElement.getElementsByTagName("CD");
    for (i=0;i<x.length;i++)
      {
      txt=txt + "<tr>";
      xx=x[i].getElementsByTagName("TITLE");
        {
        try
          {
          txt=txt + "<td>" + xx[0].firstChild.nodeValue + "</td>";
          }
        catch (er)
          {
          txt=txt + "<td>&nbsp;</td>";
          }
        }
    xx=x[i].getElementsByTagName("ARTIST");
      {
        try
          {
          txt=txt + "<td>" + xx[0].firstChild.nodeValue + "</td>";
          }
        catch (er)
          {
          txt=txt + "<td>&nbsp;</td>";
          }
        }
      txt=txt + "</tr>";
      }
    txt=txt + "</table>";
    document.getElementById('txtCDInfo').innerHTML=txt;
    }
  }
xmlhttp.open("GET",url,true);
xmlhttp.send();
}

Или я полностью отмечен здесь?

Ответ 6

Выбор 1 (если вам нужна надежная система): Комбинация сервера очередей (например, AMQP или MQTT и т.д.) И клиента websocket, такого как http://www.hivemq.com/full-featured-mqtt-client-browser/, будет надежной. Почти каждый язык поддерживает AMQP, например. в PHP (https://pecl.php.net/package/amqp).

Выбор 2: Используйте ajax и скажите браузеру, чтобы получать обновления с сервера в периодическом интервале (как уже упоминалось в комментариях).

Ответ 7

Хорошая дискуссия. Мне нравится: D @andy: отлично, для меня в первый раз кто-то может объяснить разницу реальную точность, и я получаю ее: D @Marcos Dimitrio Я согласен

Я сам запускаю довольно скверный пул нитей твитов API Twitter, которые делают именно это, за исключением того, что push_post $_POST из facebook, если я правильно понимаю. Он контролирует твиты в реальном времени для массивов сотен/тысяч кластеров ключевых слов через streamin api Firehose. В противном случае это может пойти или потерпеть страшное поражение: D ИМХО, конечно. Это половина из двух демонов, называемых getTweets и parseTweets.

<?php
ob_start();
require_once('config/phirehose-config.php');
require_once('lib.php');
$oDB = new db;

// run as a daemon aka background process
while (true) {

  // Process all statuses
  $query = 'SELECT cache_id, raw_tweet ' .
    'FROM json_cache';
  $result = $oDB->select($query);
  while($row = mysqli_fetch_assoc($result)) {

    $cache_id = $row['cache_id'];
//    $status = unserialize(base64_decode($row['raw_tweet']));
    $tweet_object = json_decode($row['raw_tweet'],false);


    // JSON payload for statuses stored in the database  
    // serialized base64 raw data

      // Delete cached copy of tweet
      //    $oDB->select("DELETE FROM json_cache WHERE cache_id = $cache_id");

        // Limit tweets to a single language,
        // such as 'en' for English
        //if ($tweet_object->lang <> 'nl') {continue;}

    // Test status update before inserting
    $tweet_id = $tweet_object->id_str;

    if ($oDB->in_table('tweets','tweet_id=' . $tweet_id )) {continue;}

    $tweet_text = $oDB->escape($tweet_object->text);    
    $created_at = $oDB->date($tweet_object->created_at);
    if (isset($tweet_object->geo)) {
      $geo_lat = $tweet_object->geo->coordinates[0];
      $geo_long = $tweet_object->geo->coordinates[1];
    } else {
      $geo_lat = $geo_long = 0;
    } 
    $user_object = $tweet_object->user;
    $user_id = $user_object->id_str;
    $screen_name = $oDB->escape($user_object->screen_name);
    $name = $oDB->escape($user_object->name);
    $profile_image_url = $user_object->profile_image_url;


    // Add a new user row or update an existing one
    $field_values = 'screen_name = "' . $screen_name . '", ' .
      'profile_image_url = "' . $profile_image_url . '", ' .
      'user_id = ' . $user_id . ', ' .
      'name = "' . $name . '", ' .
      'location = "' . $oDB->escape($user_object->location) . '", ' . 
      'url = "' . $user_object->url . '", ' .
      'description = "' . $oDB->escape($user_object->description) . '", ' .
      'created_at = "' . $oDB->date($user_object->created_at) . '", ' .
      'followers_count = ' . $user_object->followers_count . ', ' .
      'friends_count = ' . $user_object->friends_count . ', ' .
      'statuses_count = ' . $user_object->statuses_count . ', ' . 
      'time_zone = "' . $user_object->time_zone . '", ' .
      'last_update = "' . $oDB->date($tweet_object->created_at) . '"' ;     

    if ($oDB->in_table('users','user_id="' . $user_id . '"')) {
      $oDB->update('users',$field_values,'user_id = "' .$user_id . '"');
    } else {            
      $oDB->insert('users',$field_values);
    }

    // percist status to database

    $field_values = 'tweet_id = ' . $tweet_id . ', ' ....


    //... Somethings are to be for da cook alone, its hard work          

            foreach ($entities->hashtags as $hashtag) {

      $where = 'tweet_id=' . $tweet_id . ' ' .
        'AND tag="' . $hashtag->text . '"';     

      if(! $oDB->in_table('tweet_tags',$where)) {

        $field_values = 'tweet_id=' . $tweet_id . ', ' .
          'tag="' . $hashtag->text . '"';   

        $oDB->insert('tweet_tags',$field_values);
      }
    }
    foreach ($entities->urls as $url) {

      if (empty($url->expanded_url)) {
        $url = $url->url;
      } else {
        $url = $url->expanded_url;
      }

      $where = 'tweet_id=' . $tweet_id . ' ' .
        'AND url="' . $url . '"';       

      if(! $oDB->in_table('tweet_urls',$where)) {
        $field_values = 'tweet_id=' . $tweet_id . ', ' .
          'url="' . $url . '"'; 

        $oDB->insert('tweet_urls',$field_values);
      }
    }       
  } 

  if(DEBUG){ 
     echo ob_get_contents();
     ob_clean();
  }else{
     ob_clean();
  }

  // Longer sleep equals lower server load
  sleep(1);
}
?>

Также отлично подходит для пауков и сканеров, для которых есть своя собственная команда. Покажите мне лучший способ сделать это, все, что связано с ресурсами и масштабируемостью, поскольку настойчиво связанный виджет веб-сайта для обновлений статуса FB действительно похож на использование Echelon в качестве пульта tv снова imho).

Ответ 8

Если вам просто нужно простое решение и вы не беспокоитесь о более старой совместимости браузера, и у вас низкая ситуация с трафиком, события, отправленные сервером может сделать трюк.

Вы создаете экземпляр с помощью одной строки, если ваше генерирование push-сообщений script находится на одном сервере.

var evtSource = new EventSource("messages.php");

Затем функция для обработки входящих сообщений.

    evtSource.onmessage = function(e) {

       console.log(e.data);
    }

messages.php должен иметь заголовок как

header("Content-Type: text/event-stream\n\n");

Затем сделайте бесконечный цикл, заданный по желанию интервалом.

Пример:

header("Content-Type: text/event-stream\n\n");
date_default_timezone_set("Pacific/Auckland"); // as required
$sleepTime = 8; // # of seconds delayed between notifications

while (1) 
{   

   // Send a message at $sleepTime second intervals.
   echo 'data: The time is ' . date("H:i:s") . "\n\n";

   ob_end_flush();
   flush();
   sleep($sleepTime);
}

Очевидно, вам нужно будет читать сообщения откуда-то и представлять по мере необходимости, но, по крайней мере, этот пример дает вам представление о том, как создать поток событий без необходимости входить в относительную сложность WebSocket.

Отказ от ответственности: я не очень опытный PHP, но это решение кажется работать на меня. Если есть какие-то проблемы с этим, я бы хотел услышать.

Ответ 9

Отправить запрос, только когда вы вернете данные с сервера. И на сервере засыпайте, пока не найдете что-то новое для отправки и отправки ответа.

Продолжайте работу выше, пока ваша страница не будет жива, и вы увидите, что данные нажаты. Это позволяет избежать регулярного ответа сервера на сервер.

Я не уверен, ссылаются ли они на это здесь, https://en.wikipedia.org/wiki/Comet_(programming)