Lately I've been researching about technologies with which I could develop a simple chat using PHP and AJAX. Since I prefer not to install any software, I've decided to use SSE (Server Sent Events) instead of websockets.
This method allows the client who calls the php file to receive data without making a request, which is what I want, but the problem comes with the fact that every new client connecting executes the same php file again, instead of accessing the one that is already running and getting the same responses than every other client.
This means that if multiple clients calling the php file to receive data, the server will overload at some point. Knowing that, would it be possible to make every client receive the data coming only from one php executing file?
Here is what is going on:
This is okay, but as you can see, I end up having the same file executing two times, one for every client.
Here is what I want to do:
As you can see here, there is only one php file sending data, but multiple clients are receiving it.
How is this possible?
myFile.php
<?php
header('Content-Type: text/event-stream');
header('Cache-Control: no-cache');
$number = 0;
while(true){
echo "data: $number ";
echo "\n\n";
$number++;
sleep(1);
#ob_flush();
flush();
}
?>
client.html
<!DOCTYPE html>
<html>
<body>
<h1>Getting server updates</h1>
<div id="result"></div>
<script>
if(typeof(EventSource) !== "undefined") {
var source = new EventSource("myFile.php");
source.onmessage = function(event) {
document.getElementById("result").innerHTML += event.data + " ";
};
} else {
document.getElementById("result").innerHTML = "Your browser doesn´t support SSE (server-sent events)";
}
</script>
</body>
</html>
Thanks in advance.
Okay so I've finally solved the question. The answer is NO, multiple clients can't receive data from one single executing SSE script, since that would mean a huge security vulnerability for any client that uses it.
Apart from that, SSE is design to lock the session of the client who is opening the scrip until it ends or closes. This way, if some session parameter changes during the execution of the SSE, it won't read it. The only way to change session parameters during the execution, is by using the session_commit() function to commit those session changes.
This also means that if the session is locked, you the client won't be able to receive any other http response until the SSE script closes. Every response will stay on pending status until that happens.
This applies to ajax, whil also works with http asyn requests. At the time the SSE lock the session, the client will stop receiving ajax responses until the SSE script stops, or until the SSE script uses the session_commint() function.
This post helped me to understand how it works.
I want to return the final user a response, before I'm making all the hard processing data.
I want to simply get a request from user,
return a response, it could be a simple json,
and than the user will see the data, but in my server side I would continue my rest of the processing, calling analytics, changing DB and other.
something like this idea:
continue processing php after sending http response
For heavier tasks you should use the kernel.terminate Event. So any task in this event is performed, after the response was sent. This is the way, how the swiftmailer memory spool works.
This maybe a late answer, but the mentioned scenario makes a perfect use of Queues.
Using a library like LeezyPheanstalkBundle will make usage of beanstalkd a lot easier.
What happens is:
1. Receive user request
2. Add job to queue (very fast)
3. Return server response
4. a worker retrieve the job and processes it.
Check out the StreamedResponse:
$response = new StreamedResponse();
$response->setCallback(function () {
echo 'Hello World';
flush();
sleep(2);
echo 'Hello World';
flush();
});
$response->send();
Read more here: http://symfony.com/doc/current/components/http_foundation/introduction.html#streaming-a-response
While this method has its simplicity and usefulness in certain cases, it's not the best user experience in a webpage. The HTML page will only be fully loaded at the end of your script, which may cause visual annoyances and the lack of Javascript support, incomplete CSS rendering, etc...
If you're going for the best visual experience, you could load an empty page, and then make concurrent ajax calls that will do the processing. ie. each AJAX request will process 5 records and output them.
This way you can have a progress bar or some other animation going on the page...
I have been working with jquery/ajax requests. I have successfully got a ajax request which will retrieve data from a database, the problem is, that i'm constantly serving window.setInterval() to refresh this function every x amount of seconds.
How would I change this to keep the ajax request alive, so it updates the html content without having to serve multiple requests to my ajax script.
My code follows:
window.setInterval(function()
{
$(function ()
{
$.ajax({
url: 'Ajax.php'+SearchTerm, dataType: 'json', success: function(rows)
{
$('#NumberOfVotes').empty();
for (var i in rows)
{
var row = rows[i];
var QuestionID = row[0];
var Votes = row[1];
$('#NumberOfVotes')
.append(Votes);
}
}
});
});
}, 500);
A lot of this depends on how your server would be able to update it's content dynamically. That said, what you are looking for is websockets. Websockets are designed to replace the long-polling paradigm.
EDIT: Since you use mainly php for your server technology, look at Ratchet. I've heard good things about it http://socketo.me/
Here is an excellent article on using websockets with HTML
http://net.tutsplus.com/tutorials/javascript-ajax/start-using-html5-websockets-today/
.NET has a great socket library in SignalR
http://signalr.net/
There is a myriad of php documentation on sockets out there
http://php.net/manual/en/book.sockets.php
look into using web sockets - you could send the client a message anytime they need to go an look for new data - that way your not making any unnecessary requests. Try checking out pubnub -service is cheap and could handle everything you need.
You could set xhr.multipart = true and modify server code. see Multipart Responses Example Code. Alternative way is to use websockets as mentioned
You need something server side that keeps the request alive until it has something to return. This is usually called "Comet", "Long-polling" or "Push".
The principle is :
You send a request client-side via AJAX
Your server receives the request, and doesn't return a response yet. It sleeps/waits until it has something to return
A new entry in your database ! Your server now has something to return : it returns some JSON data for the waiting request
Your receive the response server side, display what you have to display, and go back to step 1 sending another request.
Now, the implementation server side will depend on the language/framework you are using.
Edit :
Some examples using PHP :
Comet and PHP
Simple Comet Implementation Using PHP and jQuery
I've seen pages like facebook where, if you post a message in your newsfeed, it automatically pushes that across your browsers. Or like on this page... if someone has answered a question while you are typing, a bar drops down.
Are they just calling AJAX requests every 30 seconds or whatever? It seems like that would be a resource drain on your server. Is there a way to push something at the browser instead?
There are 3 options here:
Use the new (experimental) browser API (sockets)
Long polling / comet
Using / listening to cookies
Long polling / comet example in PHP / AJAX
// PHP SIDE
$max_wait_time = 30; // at most, 30 seconds
$start_time = microtime(true);
while( $start_time - microtime(true) < $max_wait_time ){
// ...check if something changed (eg, run an SQL query or something)
if($something_changed){
echo 'something changed';
die;
}
// if the user did abort, terminate immediately
if( connection_aborted() ) die;
// sleep for one second. For faster responses, keep
// splitting this suitably (eg, 0.5 of a second...)
usleep(1000000);
}
// JS SIDE
var poll = function(){
jQuery.get('the url', function(){
poll();
});
}
poll();
Cookie example in PHP / JS (you need the jQuery cookie plugin)
<?php
// PHP SIDE
setcookie('test', mt_rand(0,100));
?><!-- HTML/JS SIDE -->
Rand!
Rand=<span><?php echo $_COOKIE['test']; ?></span>
<script type="text/javascript">
var oldrand = <?php echo $_COOKIE['test']; ?>;
setInterval(function(){
var newrand = jQuery.cookie('test');
if( newrand!=oldrand ){
jQuery('span').html(newrand);
oldrand = newrand;
}
}, 500);
</script>
The cookie one is pretty good for several reasons:
it is pretty fast (no AJAX calls)
it is less resource intensive on both client and server side
it consumes less bandwidth / network resources
it is much easier to control
In some cases where cookies cannot work, I'd still advocate the use of cookies as a signal to run an AJAX call, hence you wouldn't need to run a lot of AJAX calls just to wait for a change to happen.
On the other hand, the cookie one won't work when the change is happening by a third party, eg, it won't be suitable at all for chat systems.
Read into the differences between push and pull for more information:
In your example, the AJAX requests every 30 seconds would be a pull request - constantly asking the server if any updates are available, followed by a response.
You can set up a server/website to send push notifications to the client browser - whereby the client sits quietly, and the server sends the data/information to the client as soon as it is available (reducing network traffic etc.).
Push is much better in my opinion.
Yep, you'd have to poll with a looping Ajax script. To keep resource drain down, you might want to send some kind of hash (the timestamp of the last news item for instance) so the server knows if the client is up to date. This way, it can instantly return if there's no changes to push.
I can find lots of information on how Long Polling works (For example, this, and this), but no simple examples of how to implement this in code.
All I can find is cometd, which relies on the Dojo JS framework, and a fairly complex server system..
Basically, how would I use Apache to serve the requests, and how would I write a simple script (say, in PHP) which would "long-poll" the server for new messages?
The example doesn't have to be scaleable, secure or complete, it just needs to work!
It's simpler than I initially thought.. Basically you have a page that does nothing, until the data you want to send is available (say, a new message arrives).
Here is a really basic example, which sends a simple string after 2-10 seconds. 1 in 3 chance of returning an error 404 (to show error handling in the coming Javascript example)
msgsrv.php
<?php
if(rand(1,3) == 1){
/* Fake an error */
header("HTTP/1.0 404 Not Found");
die();
}
/* Send a string after a random number of seconds (2-10) */
sleep(rand(2,10));
echo("Hi! Have a random number: " . rand(1,10));
?>
Note: With a real site, running this on a regular web-server like Apache will quickly tie up all the "worker threads" and leave it unable to respond to other requests.. There are ways around this, but it is recommended to write a "long-poll server" in something like Python's twisted, which does not rely on one thread per request. cometD is an popular one (which is available in several languages), and Tornado is a new framework made specifically for such tasks (it was built for FriendFeed's long-polling code)... but as a simple example, Apache is more than adequate! This script could easily be written in any language (I chose Apache/PHP as they are very common, and I happened to be running them locally)
Then, in Javascript, you request the above file (msg_srv.php), and wait for a response. When you get one, you act upon the data. Then you request the file and wait again, act upon the data (and repeat)
What follows is an example of such a page.. When the page is loaded, it sends the initial request for the msgsrv.php file.. If it succeeds, we append the message to the #messages div, then after 1 second we call the waitForMsg function again, which triggers the wait.
The 1 second setTimeout() is a really basic rate-limiter, it works fine without this, but if msgsrv.php always returns instantly (with a syntax error, for example) - you flood the browser and it can quickly freeze up. This would better be done checking if the file contains a valid JSON response, and/or keeping a running total of requests-per-minute/second, and pausing appropriately.
If the page errors, it appends the error to the #messages div, waits 15 seconds and then tries again (identical to how we wait 1 second after each message)
The nice thing about this approach is it is very resilient. If the clients internet connection dies, it will timeout, then try and reconnect - this is inherent in how long polling works, no complicated error-handling is required
Anyway, the long_poller.htm code, using the jQuery framework:
<html>
<head>
<title>BargePoller</title>
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.2.6/jquery.min.js" type="text/javascript" charset="utf-8"></script>
<style type="text/css" media="screen">
body{ background:#000;color:#fff;font-size:.9em; }
.msg{ background:#aaa;padding:.2em; border-bottom:1px #000 solid}
.old{ background-color:#246499;}
.new{ background-color:#3B9957;}
.error{ background-color:#992E36;}
</style>
<script type="text/javascript" charset="utf-8">
function addmsg(type, msg){
/* Simple helper to add a div.
type is the name of a CSS class (old/new/error).
msg is the contents of the div */
$("#messages").append(
"<div class='msg "+ type +"'>"+ msg +"</div>"
);
}
function waitForMsg(){
/* This requests the url "msgsrv.php"
When it complete (or errors)*/
$.ajax({
type: "GET",
url: "msgsrv.php",
async: true, /* If set to non-async, browser shows page as "Loading.."*/
cache: false,
timeout:50000, /* Timeout in ms */
success: function(data){ /* called when request to barge.php completes */
addmsg("new", data); /* Add response to a .msg div (with the "new" class)*/
setTimeout(
waitForMsg, /* Request next message */
1000 /* ..after 1 seconds */
);
},
error: function(XMLHttpRequest, textStatus, errorThrown){
addmsg("error", textStatus + " (" + errorThrown + ")");
setTimeout(
waitForMsg, /* Try again after.. */
15000); /* milliseconds (15seconds) */
}
});
};
$(document).ready(function(){
waitForMsg(); /* Start the inital request */
});
</script>
</head>
<body>
<div id="messages">
<div class="msg old">
BargePoll message requester!
</div>
</div>
</body>
</html>
I've got a really simple chat example as part of slosh.
Edit: (since everyone's pasting their code in here)
This is the complete JSON-based multi-user chat using long-polling and slosh. This is a demo of how to do the calls, so please ignore the XSS problems. Nobody should deploy this without sanitizing it first.
Notice that the client always has a connection to the server, and as soon as anyone sends a message, everyone should see it roughly instantly.
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<!-- Copyright (c) 2008 Dustin Sallings <dustin+html#spy.net> -->
<html lang="en">
<head>
<title>slosh chat</title>
<script type="text/javascript"
src="http://code.jquery.com/jquery-latest.js"></script>
<link title="Default" rel="stylesheet" media="screen" href="style.css" />
</head>
<body>
<h1>Welcome to Slosh Chat</h1>
<div id="messages">
<div>
<span class="from">First!:</span>
<span class="msg">Welcome to chat. Please don't hurt each other.</span>
</div>
</div>
<form method="post" action="#">
<div>Nick: <input id='from' type="text" name="from"/></div>
<div>Message:</div>
<div><textarea id='msg' name="msg"></textarea></div>
<div><input type="submit" value="Say it" id="submit"/></div>
</form>
<script type="text/javascript">
function gotData(json, st) {
var msgs=$('#messages');
$.each(json.res, function(idx, p) {
var from = p.from[0]
var msg = p.msg[0]
msgs.append("<div><span class='from'>" + from + ":</span>" +
" <span class='msg'>" + msg + "</span></div>");
});
// The jQuery wrapped msgs above does not work here.
var msgs=document.getElementById("messages");
msgs.scrollTop = msgs.scrollHeight;
}
function getNewComments() {
$.getJSON('/topics/chat.json', gotData);
}
$(document).ready(function() {
$(document).ajaxStop(getNewComments);
$("form").submit(function() {
$.post('/topics/chat', $('form').serialize());
return false;
});
getNewComments();
});
</script>
</body>
</html>
Tornado is designed for long-polling, and includes a very minimal (few hundred lines of Python) chat app in /examples/chatdemo , including server code and JS client code. It works like this:
Clients use JS to ask for an updates since (number of last message), server URLHandler receives these and adds a callback to respond to the client to a queue.
When the server gets a new message, the onmessage event fires, loops through the callbacks, and sends the messages.
The client-side JS receives the message, adds it to the page, then asks for updates since this new message ID.
I think the client looks like a normal asynchronous AJAX request, but you expect it to take a "long time" to come back.
The server then looks like this.
while (!hasNewData())
usleep(50);
outputNewData();
So, the AJAX request goes to the server, probably including a timestamp of when it was last update so that your hasNewData() knows what data you have already got.
The server then sits in a loop sleeping until new data is available. All the while, your AJAX request is still connected, just hanging there waiting for data.
Finally, when new data is available, the server gives it to your AJAX request and closes the connection.
Here are some classes I use for long-polling in C#. There are basically 6 classes (see below).
Controller: Processes actions required to create a valid response (db operations etc.)
Processor: Manages asynch communication with the web page (itself)
IAsynchProcessor: The service processes instances that implement this interface
Sevice: Processes request objects that implement IAsynchProcessor
Request: The IAsynchProcessor wrapper containing your response (object)
Response: Contains custom objects or fields
This is a nice 5-minute screencast on how to do long polling using PHP & jQuery:
http://screenr.com/SNH
Code is quite similar to dbr's example above.
Here is a simple long-polling example in PHP by Erik Dubbelboer using the Content-type: multipart/x-mixed-replace header:
<?
header('Content-type: multipart/x-mixed-replace; boundary=endofsection');
// Keep in mind that the empty line is important to separate the headers
// from the content.
echo 'Content-type: text/plain
After 5 seconds this will go away and a cat will appear...
--endofsection
';
flush(); // Don't forget to flush the content to the browser.
sleep(5);
echo 'Content-type: image/jpg
';
$stream = fopen('cat.jpg', 'rb');
fpassthru($stream);
fclose($stream);
echo '
--endofsection
';
And here is a demo:
http://dubbelboer.com/multipart.php
I used this to get to grips with Comet, I have also set up Comet using the Java Glassfish server and found lots of other examples by subscribing to cometdaily.com
Take a look at this blog post which has code for a simple chat app in Python/Django/gevent.
Below is a long polling solution I have developed for Inform8 Web. Basically you override the class and implement the loadData method. When the loadData returns a value or the operation times out it will print the result and return.
If the processing of your script may take longer than 30 seconds you may need to alter the set_time_limit() call to something longer.
Apache 2.0 license. Latest version on github
https://github.com/ryanhend/Inform8/blob/master/Inform8-web/src/config/lib/Inform8/longpoll/LongPoller.php
Ryan
abstract class LongPoller {
protected $sleepTime = 5;
protected $timeoutTime = 30;
function __construct() {
}
function setTimeout($timeout) {
$this->timeoutTime = $timeout;
}
function setSleep($sleep) {
$this->sleepTime = $sleepTime;
}
public function run() {
$data = NULL;
$timeout = 0;
set_time_limit($this->timeoutTime + $this->sleepTime + 15);
//Query database for data
while($data == NULL && $timeout < $this->timeoutTime) {
$data = $this->loadData();
if($data == NULL){
//No new orders, flush to notify php still alive
flush();
//Wait for new Messages
sleep($this->sleepTime);
$timeout += $this->sleepTime;
}else{
echo $data;
flush();
}
}
}
protected abstract function loadData();
}
This is one of the scenarios that PHP is a very bad choice for. As previously mentioned, you can tie up all of your Apache workers very quickly doing something like this. PHP is built for start, execute, stop. It's not built for start, wait...execute, stop. You'll bog down your server very quickly and find that you have incredible scaling problems.
That said, you can still do this with PHP and have it not kill your server using the nginx HttpPushStreamModule: http://wiki.nginx.org/HttpPushStreamModule
You setup nginx in front of Apache (or whatever else) and it will take care of holding open the concurrent connections. You just respond with payload by sending data to an internal address which you could do with a background job or just have the messages fired off to people that were waiting whenever the new requests come in. This keeps PHP processes from sitting open during long polling.
This is not exclusive to PHP and can be done using nginx with any backend language. The concurrent open connections load is equal to Node.js so the biggest perk is that it gets you out of NEEDING Node for something like this.
You see a lot of other people mentioning other language libraries for accomplishing long polling and that's with good reason. PHP is just not well built for this type of behavior naturally.
Thanks for the code, dbr. Just a small typo in long_poller.htm around the line
1000 /* ..after 1 seconds */
I think it should be
"1000"); /* ..after 1 seconds */
for it to work.
For those interested, I tried a Django equivalent. Start a new Django project, say lp for long polling:
django-admin.py startproject lp
Call the app msgsrv for message server:
python manage.py startapp msgsrv
Add the following lines to settings.py to have a templates directory:
import os.path
PROJECT_DIR = os.path.dirname(__file__)
TEMPLATE_DIRS = (
os.path.join(PROJECT_DIR, 'templates'),
)
Define your URL patterns in urls.py as such:
from django.views.generic.simple import direct_to_template
from lp.msgsrv.views import retmsg
urlpatterns = patterns('',
(r'^msgsrv\.php$', retmsg),
(r'^long_poller\.htm$', direct_to_template, {'template': 'long_poller.htm'}),
)
And msgsrv/views.py should look like:
from random import randint
from time import sleep
from django.http import HttpResponse, HttpResponseNotFound
def retmsg(request):
if randint(1,3) == 1:
return HttpResponseNotFound('<h1>Page not found</h1>')
else:
sleep(randint(2,10))
return HttpResponse('Hi! Have a random number: %s' % str(randint(1,10)))
Lastly, templates/long_poller.htm should be the same as above with typo corrected. Hope this helps.
Why not consider the web sockets instead of long polling? They are much efficient and easy to setup. However they are supported only in modern browsers. Here is a quick reference.
The WS-I group published something called "Reliable Secure Profile" that has a Glass Fish and .NET implementation that apparently inter-operate well.
With any luck there is a Javascript implementation out there as well.
There is also a Silverlight implementation that uses HTTP Duplex. You can connect javascript to the Silverlight object to get callbacks when a push occurs.
There are also commercial paid versions as well.
For a ASP.NET MVC implementation, look at SignalR which is available on NuGet.. note that the NuGet is often out of date from the Git source which gets very frequent commits.
Read more about SignalR on a blog on by Scott Hanselman
You can try icomet(https://github.com/ideawu/icomet), a C1000K C++ comet server built with libevent. icomet also provides a JavaScript library, it is easy to use as simple as
var comet = new iComet({
sign_url: 'http://' + app_host + '/sign?obj=' + obj,
sub_url: 'http://' + icomet_host + '/sub',
callback: function(msg){
// on server push
alert(msg.content);
}
});
icomet supports a wide range of Browsers and OSes, including Safari(iOS, Mac), IEs(Windows), Firefox, Chrome, etc.
Simplest NodeJS
const http = require('http');
const server = http.createServer((req, res) => {
SomeVeryLongAction(res);
});
server.on('clientError', (err, socket) => {
socket.end('HTTP/1.1 400 Bad Request\r\n\r\n');
});
server.listen(8000);
// the long running task - simplified to setTimeout here
// but can be async, wait from websocket service - whatever really
function SomeVeryLongAction(response) {
setTimeout(response.end, 10000);
}
Production wise scenario in Express for exmaple you would get response in the middleware. Do you what you need to do, can scope out all of the long polled methods to Map or something (that is visible to other flows), and invoke <Response> response.end() whenever you are ready. There is nothing special about long polled connections. Rest is just how you normally structure your application.
If you dont know what i mean by scoping out, this should give you idea
const http = require('http');
var responsesArray = [];
const server = http.createServer((req, res) => {
// not dealing with connection
// put it on stack (array in this case)
responsesArray.push(res);
// end this is where normal api flow ends
});
server.on('clientError', (err, socket) => {
socket.end('HTTP/1.1 400 Bad Request\r\n\r\n');
});
// and eventually when we are ready to resolve
// that if is there just to ensure you actually
// called endpoint before the timeout kicks in
function SomeVeryLongAction() {
if ( responsesArray.length ) {
let localResponse = responsesArray.shift();
localResponse.end();
}
}
// simulate some action out of endpoint flow
setTimeout(SomeVeryLongAction, 10000);
server.listen(8000);
As you see, you could really respond to all connections, one, do whatever you want. There is id for every request so you should be able to use map and access specific out of api call.