Comet. Apache + php + Nodejs + Socket.io. Realtime fullduplex web application
Классическая модель интернета - запрос - ответ не позволяет строить полноценные приложения, в которых сервер может отсылать данные клиентам в браузер. Однако существует множество технологий позволяющих тем или иным путем симулировать или добиться настоящей обратной связи, к примеру для построения чата с моментальным откликом на каком-либо веб ресурсе. В статье приведен пример как обычный сайт, использующий в качестве сервера - Apache, можно наполнить динамикой используя современные технологии протокола Websocket, библиотеку js socket.io и серверное приложение, написанное на Nodejs исползуемого Apache'ем в связке с Curl.
Краткая теория как это работает:
Известно что начиная с версии прокола http 1.0 браузеры при отправке запросов сохраняют TCP туннель, и используют его снова для отправки нового запроса, поскольку TCP является протоколом транспортного уровня он предоставляет возможность серверу послать обратный запрос, к сожалению http протокол данной возможности не предоставляет, но сам браузер - способен принять ответ по TCP туннелю и так же как в случае с ajax передать ответь в js функцию которая решит что с этим ответом делать.
Отсюда следует - что для того чтобы обеспечить возможность серверу отсылать в браузер данные нужно воспользоваться API бразуера с помощью js - в этом нам поможет библиотека socket.io, а для того чтобы сервер сумел отправить нам данные - там должен находиться иной сервер (отличный от apache) - способный использовать протокол отличный от http - а именно web socket - для примера был выбран движок Nodejs и написанное на нем серверное приложение, которое будет принимать запросы от браузеров, поддерживать с ними постоянные связи, а при поступлении новых данных на сервер apach'a, отсылать подключенным клиентам в браузер свежую информацию помощью приложения nodejs.
Так же в начале упоминалось про apache - чтобы не переписывать на nodejs весь старый функционал сайта, а просто добавить новую фичу типа чата (пример будет простым, просто описывающим принцип работы) - мы будем использовать apache и когда на сервер будет приходить информация - например новый пост - будет срабатывать Curl - который обычным http запросом с постом будет слать данные на nodejs приложение, которое в свою очередь будет обновлять клиентам данные:
Итак начнем: вначале на сервер apach'а приходит запрос и он возвращает страничку, на которой вставлен js скрипт:
// Подключаем библиотеку для установления // постоянного конекта с nodejs приложеним. <script src="socket.io.js"></script> <script> var sw='run'; // Конектимся на локальный сервер по 8080 порту. var socket = io.connect('http://localhost:8080'); // Функция которая сработает когда нам по туннелю // отправят данные как и в случае с ajax. socket.on('stream', function (data) { // Для наглядности вставим ответ внутрь враппера. document.getElementById('number').innerHTML = data.n; }); function stop_timer() { if (sw == 'run') { // Шлем данные на nodejs приложение по клику. socket.emit('action', {todo: 'stop'}); sw='stop'; } else { // Шлем данные на nodejs приложение по клику. socket.emit('action', { todo: 'run'}); } } </script> Magic Page <div style="border:1px solid #ccc" id="number"> </div> <a href="#" onclick="stop_timer();return false;">Action</a>
Вышеописанный скрипт устанавливает постоянный TCP туннель через Websocket протокол со нижеописанным nodejs приложением написанным на javascript с использованием нескольких обычных модулей\расширений, которые можно установить с помощью утилиты npm:
Nodejs приложение биндит сокет по 8080 порту, и когда бразуер устанавливает с ним сообщение, он единожды биндит другой сокет на другой порт - 8300 (поднимает http сервер), и когда на вторичный адрес приходит запрос отдает всем участникам (пользователям которые установили постоянные соединения) какие-то данные.
// Подключаем библиотеку и сразу же поднимаем сервер на 8080 порт. var io = require('...path/node_modules/socket.io').listen(8080); // Load the http module to create an http server. var http = require('http'); var server = undefined; io.sockets.on('connection', function (socket) { // Счетчик для примера. t = 1; // Создаем HTTP server если он не был создан. if (typeof(server) == 'undefined') { server = http.createServer(function (request, response) { response.writeHead(200, {"Content-Type": "text/plain"}); // На запрос вызываем метод библиотеки socket.io // чтобы отослать данные подключенным постоянно клиентам. socket.broadcast.emit('stream', {n:t = t+1}); // Выводим сообщение. response.end("Hello World\n"); }); } // Биндим Http server на 8300 порт, IP defaults to 127.0.0.1 server.listen(8300); // Отсылаем всем клиентам какие-то данные при // подключении нового клиента. socket.broadcast.emit('stream', {n:'c'}); socket.on('action', function (data) { if (data.todo=='stop') { // Что-нибудь сделать на action stop } else if (data.todo = 'run') { // Что-нибудь сделать на action run } }); });
Собственно все уже работает, остается из apacha на php вызвать к примеру с помощью curl http запрос на такой-то порт (8300) такой-то адрес (указанный в nodejs приложении) и тогда nodejs примет его и передаст данные на клиентов.