Websockets : PHP code complete tutorial client & server

25 Jun 2013 | Websockets : PHP code complete tutorial client & server |

html5-websocketsStep-by-step to build a simple HTML5 using a PHP server for true server-push websocket communications

I’m going to demo how you can create a simple and easy echo server, and communicate bidirectionally with various web clients.  The ability for the server to send (or “push“) communications to the client (browser) at any moment provides a whole new level of capabilities, especially for such web applications and services such as… Games, RIA applications, streaming tickers, chat , or anything requiring instant messaging communications etc. Prior to web sockets the only practical way to create server communications was to use long polling, or some browser plugin that provided Comet support.

[icon name=”paper-plane” class=”” unprefixed_class=””] UPDATED: If you just need simpler one-way data streaming there’s a  better and easier way to do this is via Server Sent Events, Check out my recent post.

HTML5 is likely to go down in web history as the most important revision to HTML , well since  HTML first introduced the <img > tag. HTML5 is jam-packed with all sorts of useful features. Just head on over to HTML 5 Rocks to find out the latest and greatest. in this post I am going to focus on one of HTML5 newer capabilities Websockets.

GitHub source:

You can find the all the code here: https://github.com/acbrandao/PHP/tree/master/ws

What are websockets?

The HTLM5  WebSocket  API (technically its not a true HTML5 standard as its still in a state of flux as described  here) , but in 2014 for the most part it does work as advertised in modern browsers. The Websocket API allows for establishing specialized “socket” connections between a web browser and a server. This means that  there is an persistent connection between the client and the server and it is a bi-direction low latency connection that allows the server to asynchronously send messages to the client and vice-versa. The standard also supports ping/pong (heartbeat)  frames that allow either the client to verify the status of the connection. For a  more complete websocket description try this link.

Websocket client platform support and usage

Because websocket is relatively  new  HTMl5 capability, it goes without say that its up to the browser vendors to provide an implementation.  Most modern browsers Chrome, FireFox, Opera, Safari, WebKit and others  (a few years old) have websocket support , with IE  only IE 10 or higher will support websockets, . For a more complete summary of current browser support see Wikipedia. Here’s the current (As of late 2014) supported browser (since the Version listed) source: canIsue:

Supports Websockets IE Firefox Chrome Safari Opera iOS Safari Android Browser Chrome (Android)
as of Version= 10.0  31  39 7  26  7.1   4.4 (37) Ver. 29

Once you confirmed your browser supports Websockets, the next step is to find a place you run a server that supports the websocket standard. Websockets are similar to regular socket calls, but do have a standardized handshake procedure that is HTTP compatible.

You can write a websocket server in almost any popular programming language languages, I will use a simple PHP based echo (chat) server below. Keep in mind that the server must be running as a continuous  process  in order for it to communicate with any client that may connect to it.

Websocket Client (HTML page)

So let’s get to it. Again first confirm you are using a supporting browser (the sample HTML code provides  a method to test this): The first step is to write the HTML page that will make use of Websockets. This client will represent a simple chat window, where data entered into the text field below the textarea is displayed when it’s echo’ed back by the server, also there is a list of a few  commands like id,date, time, users, temp that the server responds to.

The key part of this page is the websocket connection this is done using  new Websocket command which creates a websocket object in the browser and then we connect to a server. Optionally you can list the protocol type for websocket communication as the second parameter. After  creating the websocket specify the server (and optionally port) to communication on. Websockets have two protocols for the URL ws://servername  (defaults to port 80)  or wss://servername (Secured HTTPS socket). All this happens in the init() function.

function init(){

  host="ws://"+host+":"+port;
  url=host;
  console.log("Connecting to "+host+" url:"+url);
  document.getElementById("surl").innerText =url;
      log('trying WebSocket - : '+url);
  try{
    socket = new WebSocket(host);
    log('WebSocket - status '+socket.readyState);
    socket.onopen    = function(msg){ log("Welcome - status "+this.readyState); };
    socket.onmessage = function(msg){ 
   console.log("Ws-data"+msg);
   log("Server>: "+msg.data);

   };
    socket.onclose   = function(msg){ log("Disconnected - status "+this.readyState); };
  }
  catch(ex){ log(ex); }
  $("msg").focus();
}

In this example most of the output results is sent to a text area named  <div id=”log“></div>. Below is the complete client page, some CSS is there to make it more user-friendly.

<!-- 
*  Simple HTML websocket Client
*  original source: https://github.com/ghedipunk/PHP-WebSockets
*  this modified source: http://www.abrandao.com  
*  
*/

-->
<html>
<head>
<title>PHP Websocket Client</title>
<style>
 html,body{font:normal 0.9em arial,helvetica;}
 #log {width:440px; height:200px; border:1px solid #7F9DB9; overflow:auto;}
 #msg {width:330px;}
 .alert-box { width:75%;   color:#555;  border-radius:5px;   font-family:Tahoma,Geneva,Arial,sans-serif;font-size:11px;
    padding:5px 5px 5px 10px;    margin:10px;}
.alert-box span {    font-weight:bold;    text-transform:uppercase;}
.error {   background:#ffecec  ;  border:1px solid #f5aca6;}
.success {  background:#e9ffd9 ;   border:1px solid #a6ca8a; }
.warning { background:#fff8c4 ;	border:1px solid #f2c779;	}
.notice { background:#e3f7fc ;border:1px solid #8ed9f6;	}
</style>
</head>
<body onload="connect();">
 <h3>HTML5 WebSocket Client connecting to <span id="surl"> </span> </h3>
 <script>
  if ("WebSocket" in window)
  {
    document.write("<div class='alert-box success'><span>WebSockets supported!</span> Your browser can do websockets </div>");
  }
  else
  {
   // the browser doesn't support WebSockets
   document.write("<div class='alert-box error'><span>Error: No Websockets supported</span> Your Browser: "+ navigator.userAgent +" does not support websockets. Upgrade to modern browser </div>");
  }
  </script>
 
 <div id="log"></div>
 <input id="msg" type="textbox" onkeypress="onkey(event)"/>
 
 <button onclick="send()">Send</button>
 <button onclick="quit()">Quit</button><bR>
 <hr>
 <div>Commands:  hello,name,temp, age, date, time, thanks,id,users, bye</div>
 <hr>
 <div class='alert-box notice'><span>Update to your server</span> Change values below to match your server</div>
 Host:<input id="host" type="textbox" size="35" value="echo.websocket.org"/>
 Port:<input id="port" type="textbox" size="4" value="80"/>
 <button onclick="connect()">Re-connect</button>
<Hr>
 More details at:<a href="http://www.abrandao.com/2013/06/25/websockets-html5-php/">websockets abrandao.com</a>
 <script>
var socket;
var url = null;
var host= null;
var port=null;
var path=null;

function connect()
{
host=document.getElementById("host").value;
port=document.getElementById("port").value;
console.log("Connecting to "+host+":"+port);
 init();
}

function init(){

  host="ws://"+host+":"+port;
  url=host;
  console.log("Connecting to "+host+" url:"+url);
  document.getElementById("surl").innerText =url;
      log('trying WebSocket - : '+url);
  try{
    socket = new WebSocket(host);
    log('WebSocket - status '+socket.readyState);
    socket.onopen    = function(msg){ log("Welcome - status "+this.readyState); };
    socket.onmessage = function(msg){ 
   console.log("Ws-data"+msg);
   log("Server>: "+msg.data);

   };
    socket.onclose   = function(msg){ log("Disconnected - status "+this.readyState); };
  }
  catch(ex){ log(ex); }
  $("msg").focus();
}

function send(){
  var txt,msg;
  txt = $("msg");
  msg = txt.value;
  if(!msg){ alert("Message can not be empty"); return; }
  txt.value="";
  txt.focus();
  try
    {
 	socket.send(msg); 
  log('>>: '+msg); } 
  catch(ex)
  { log(ex); 	}
}

function quit(){
  log("Goodbye! "+url);
  socket.close();
  socket=null;
}

// Utilities
function $(id)
  { return document.getElementById(id); }
  
function log(msg){ 
  $("log").innerHTML+="<br>"+msg; 
  var textarea = document.getElementById('log');
  textarea.scrollTop = textarea.scrollHeight; //scroll to bottom
  }
  
function onkey(event){ if(event.keyCode==13){ send(); } }
</script>
 </body>
</html>

That’s it for the client, to see it in action first setup your server (See below), get it running, then change the default ip and port values in the clients…

Now let’s discuss the more complicated part of web sockets , building the server, in PHP in our case.

Websockets server basic requirements.

Writing the web socket server is a bit more involved. This is mostly because the websocket protocol goes beyond just simple sockets, its actually a multi-step process that requires a handshake, and upgrade step, then properly encoding and decoding socket data frames, plus potentially dealing with authentication and proxy issues. On top of that the actual data-frames themselves are designed to be very minimal and lightweight, thus you really need an application level protocol (like JSON, XML or even a binary protocol )  to sit above them.

While I’ll explain a basic PHP websocket server below, keep in mind this is really only a very simple server, its more of a proof of concept, and although you can tailor it in limited production environments, PHP IS NOT THE BEST LANGUAGE TO RUN A WEBSOCKET SERVER (for a variety of reasons, but primarily because of resource usage and the fact that php scripts are not designed to be a long-running processes aka a daemon)…, . If your going to be handling thousands of bi-directional connections your better off using a more established web-socket server technology such as these listed below, with node.js being the most popular implementation.

Some of  today’s popular web socket server technologies:

Server language / Framework  Packages supporting Websockets
node.js ws / socket.io / sockjs
Perl mojolicio / Net::WebSocket::Server
PHP Ratchet / PHP-Push-WebSocket /socketo.mePHP-Websockets
Python websockets / ws4py
Ruby websocket-rails / em-websocket / webmachine-ruby
Java Tomcat WebSocket How To
Web-logic Using WebSockets in WebLogic Server
Cloud-based third-party (cloud based) web-socket like Pusher
Messaging  AutoBahn open-source Web Socket Messaging protocol (python/ C++/Android/ JS)
  • There are still  many more available and you can write the server in many languages, such as Java , C# , .NET,  python , etc.

Here are a few essential server requirements that MUST be met before you can run the server.

  • The PHP script must be able to listen to (bind) to an available port on the server that it is running. Generally  it is recommend you run it on a standard port like 80/443  on its own box. (no competing web server) . Binding it to listen to the standard web ports (80/443) is preferably because it gets around proxy and firewall issues, that you are likely to encounter in production environments (This is how all the “big boys” do it).
  • If your going to use a non-standard port , very important be sure to verify its not blocked by your server’s firewall, and configure your router properly to port- forward any requests to your server on that new port. I have included a socket_only php server to help you test this.
  • Generally , NO you cannot run a web-socket server and a web server on the same ports (80,443) on the same IP at the same time, you need to choose from one of the two  options above. Either run it on a seperate ip (server) on the standard ports (80  or 443) or run it on the same box at a different unused port (preferably > 1024) . This might mean you can’t run websockets servers  on shared or reseller accounts.
    • (sidenote: there are some ways around this like running a specialized web server that natively supports web sockets like GlassFish  or configuring Apache via plugins to provide a tunnel for the websockets server ). I generally don’t recommend using this approach you introduce more complexity into your architecture and it will make debugging socket issues a bit more complex.
  • Additionally  in PHP You must be able to run the server as  continuous listening process, on Linux this requires access to the command line, You can’t run this ws_server.php  as a regular PHP web page, because all PHP scripts have  timeout limits (The default limit is 30 seconds or, if it exists, the max_execution_time value defined in the php.ini.)  PHP scripts on the command line do not have this issue.

my PHP WebSocket Server implementation

phpWebSocket server

phpWebSocket server

The example PHP server below makes use of the bare minimum protocol and provides the very basic echo functionality. To run it , you  need access to a server  (command line ssh ) where you can start the server using:
$  php  ws_server.php
by default the client.html page connection will point to a Internet  Echo server at  html5rocks.websocket.org/echo for example and testing purposes. but be sure to change to point to your local server.

If everything goes well you should be able to start your server, and then open up the client.html (reminder: but be sure to change the html  to point to your local server ) in  your browser with the client (HTML) and have a simple chat (echo ) server running

I’ll detail the key parts of the server with these code snippets, check the bottom of this posting for a complete demo and source.

The first is the phpWebSocket class that does the bulk of the work. In the ws_Server.php file I instantiate an object of this class and override the process() method to handle the actual echoing.

In the constructor we bind to the specified IP address and port, and show a console-firendly banner and server status, and indicate we’re waiting for connections. Then we enter the main loop .
while( true } { //do socket communication } , in this loop we await to see if we get a new connected clients. the sequence of this loop is as follows:

  • IF we get a new connection on this channel, lets call the connect($socket)  method. This method outlined below instatiantes a new Socket and User object, and adds them to an array of sockets and users so we can keep track of them.
function connect($socket){
  $user = new User();
  $user->id = uniqid();
  $user->socket = $socket;
  array_push($this->users,$user);
  array_push($this->sockets,$socket);
  $this->say($socket." CONNECTED!");

}

Now that we have a connection we go about the steps of initiating a handshake, followed by forming the data-frames.

IF a new client is requesting a brand -new connection we call the $this->dohandshake($user,$buffer) method which handles the websocket handshake and upgrade response. The upgrade response is what the browser is looking for so that it knows to transition this connection to a websocket connection, in other words we’re moving from HTTP to WebSocket protocol.

function dohandshake($user,$buffer){
  $this->say("\nWS Requesting handshake...");
  list($resource,$host,$origin,$key1,$key2,$l8b) = $this->getheaders($buffer);
 	$ws_magic_string="258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
//Calculate Accept = base64_encode( SHA1( key1 +attach magic_string ))
 $accept=$this->calcKey($key1,$ws_magic_string);
  
  /*
    Respond only when protocol specified in request header
    "Sec-WebSocket-Protocol: chat" . "\r\n" .
    */
$upgrade = "HTTP/1.1 101 Switching Protocols\r\n".
                 "Upgrade: websocket\r\n".
                 "Connection: Upgrade\r\n".
          "WebSocket-Location: ws://" . $host . $resource . "\r\n" .
        "Sec-WebSocket-Accept: $accept".
                 "\r\n\r\n";
        
  socket_write($user->socket,$upgrade);
$this->say("Issuing websocket Upgrade \n");
  $user->handshake=true;

  $this->say("Done handshaking... User count:".count( $this->users));
  return  $user->handshake;
}

The handshake method computes the Sec-WebSocket-Accept key based partially from the Sec-WebSocket-Key passed to it from the browser via the getheaders function.  For a more in-depth explanation of this handshake step check out the official RFC6455 on websocket server handlshake. Once the handshake is complete the Upgrade HTTP headers are sent to the browser, which if all goes well will now establish a websocket channel with the server at the specified location . Also keep in mind there’s an optional parameter that the client can request called Sec-WebSocket-Protocol this is useful if the client is wants to communicate over a standardized protocol like XML, JSON SOAP etc. part of the RFC requires you to echo back this in the Upgrade and have your server than respond to commands via this requested protocol.

Once the connection and upgrade is established, we now can move to the process() method  (which you should override in phpwebsocket.php and in this example is overridden in the ws_server.php file). Here we  are just showing the stub, but the idea is this method is what handles you server’s core logic.

function process($user,$msg){
    /* Extend and modify this method to suit your needs */
    /* Basic usage is to echo incoming messages back to client */
     $this->send($user->socket,$msg);
  }

You can see the entire logic flow of the main loop here:

function __construct($address,$port){
 // error_reporting(E_ALL);
   set_time_limit(0);
   ob_implicit_flush();

   $this->master=socket_create(AF_INET, SOCK_STREAM, SOL_TCP)     or die("socket_create() failed");
   socket_set_option($this->master, SOL_SOCKET, SO_REUSEADDR, 1)  or die("socket_option() failed");
   socket_bind($this->master, $address, $port)                    or die("socket_bind() failed");
   socket_listen($this->master,20)                                or die("socket_listen() failed");
   $this->sockets[] = $this->master;

 $this->say($this->ascii_banner() );
 $this->say("PHP WebSocket Server running....");
   $this->say("Server Started : ".date('Y-m-d H:i:s'));
   $this->say("Listening on   : ".$address." port ".$port);
   $this->say("Master socket  : ".$this->master."\n");
 $this->say(".... awaiting connections ...");
 
 // Main Server processing loop
   while(true)  //server is always listening
 {
     $changed = $this->sockets;
     socket_select($changed,$write=NULL,$except=NULL,NULL);
   $this->say("listening...\n");
     foreach($changed as $socket)
   {

       if($socket==$this->master){
         $client=socket_accept($this->master);
         if($client<0){ $this->log("socket_accept() failed"); continue; }
         else{ $this->connect($client); }
       }
       else{
         $bytes = @socket_recv($socket,$buffer,2048,0);
         if($bytes==0)
       { 
      $this->disconnect($socket); 
      }
         else{
           $user = $this->getuserbysocket($socket);
           if(!$user->handshake)
       { 
       $this->say("Handshaking $user");
       $this->dohandshake($user,$buffer);
			  }
           else
      { 
      $this->process($user,$this->frame_decode($buffer) ); 
      } 
         }
       }
     } //foreach socket
   } //main loop
 } //function

Dealing with Sending and Receiving data frames.

Once we have  a connection the real fun begins, the Websocket protocol has been designed to be very light with minimal metadata overhead protocol and because of this everything is broken down into data frames following this wire-format.

+-+-+-+-+-------+-+-------------+-------------------------------+
0                   1                   2                   3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-------+-+-------------+-------------------------------+
|F|R|R|R| opcode|M| Payload len |    Extended payload length    |
|I|S|S|S|  (4)  |A|     (7)     |             (16/64)           |
|N|V|V|V|       |S|             |   (if payload len==126/127)   |
| |1|2|3|       |K|             |                               |
+-+-+-+-+-------+-+-------------+ - - - - - - - - - - - - - - - +
|     Extended payload length continued, if payload len == 127  |
+ - - - - - - - - - - - - - - - +-------------------------------+
|                               |Masking-key, if MASK set to 1  |
+-------------------------------+-------------------------------+
| Masking-key (continued)       |          Payload Data         |
+-------------------------------- - - - - - - - - - - - - - - - +
:                     Payload Data continued ...                :
+ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +
|                     Payload Data continued ...                |
+---------------------------------------------------------------+

The details of these data packets which can be both text or binary are a bit involved and again I suggest you refer to the RFC6455 on websocket . Basically in a nutshell the various bits of each frame indicate the frame type (text, binary, final, continuing frame etc.) , and dependent on the frame size are masked , finally these bits need to be properly encoded and passed to the browser. This is done with two functions in this script frame_decode (when receiving from the browser) and frame_encode (when sending to the browser).

function frame_encode($message) {

 $length = strlen($message);

 $bytesHeader = [];
 $bytesHeader[0] = 129; // 0x1 text frame (FIN + opcode)

 if ($length <= 125) {
 $bytesHeader[1] = $length;
 } else if ($length >= 126 && $length <= 65535) {
 $bytesHeader[1] = 126;
 $bytesHeader[2] = ( $length >> 8 ) & 255;
 $bytesHeader[3] = ( $length ) & 255;
 } else {
 $bytesHeader[1] = 127;
 $bytesHeader[2] = ( $length >> 56 ) & 255;
 $bytesHeader[3] = ( $length >> 48 ) & 255;
 $bytesHeader[4] = ( $length >> 40 ) & 255;
 $bytesHeader[5] = ( $length >> 32 ) & 255;
 $bytesHeader[6] = ( $length >> 24 ) & 255;
 $bytesHeader[7] = ( $length >> 16 ) & 255;
 $bytesHeader[8] = ( $length >> 8 ) & 255;
 $bytesHeader[9] = ( $length ) & 255;
 }

 //apply chr against bytesHeader , then prepend to message
 $str = implode(array_map("chr", $bytesHeader)) . $message;
 return $str;
} 
 
 /**
 * frame_decode (decode data frame) a received payload (websockets)
 * @param $payload (Refer to: https://tools.ietf.org/html/rfc6455#section-5 )
 */
 function frame_decode($payload) 
 {
 if (!isset($payload))
 return null; //empty data return nothing

 $length = ord($payload[1]) & 127;

 if($length == 126) {
 $masks = substr($payload, 4, 4);
 $data = substr($payload, 8);
 }
 elseif($length == 127) {
 $masks = substr($payload, 10, 4);
 $data = substr($payload, 14);
 }
 else {
 $masks = substr($payload, 2, 4);
 $data = substr($payload, 6);
 }

 for ($i = 0; $i < strlen($masks); ++$i) {
 $this->say("header[".$i."] =". ord($masks[$i]). " \n");
 }
 //$this->say(" data:$data \n");
 
 //did we just get a PING frame
 if (strlen($masks)==4 && strlen($data)==0) 
 {
 return "ping";
 }
 
 $text = '';
 for ($i = 0; $i < strlen($data); ++$i) {
 $text .= $data[$i] ^ $masks[$i%4];
 }
 return $text;
} //end of frame_decode unmask(Received from client)


Keep in mind these are just the bare-essentials for encoding/decoding text data, generally a higher level protocol will sit on top of this (Read more below on messaging protocols).  Once this is done the actual sending to the client (browser) is pretty easy.

function send($client,$msg){ 
    $msg = $this->frame_encode($msg);
    socket_write($client, $msg);
    $this->say("> ".$msg." (".strlen($msg). " bytes) \n");
  }

Broadcasting to all clients – a simplistic approach

This php server example is very basic and just echoes back the typed message from the connected client, or  replies  back a specific server status like time. But one common practical feature most socket (instant message) servers implement is a broadcast (send to all clients at once). While I didn’t include that in the download (to keep the example simpler and cleaner) I will detail it here.

First you will need to change the default socket_select line to add a timeout value , by default the line below with the last two parameters of  NULL,NULL awaits until activity happens on the any of the connected sockets before proceeding to the next line, in essence it blocks and waits forever (for a connection), but this does not cause PHP to take up any needed resources as this is all interrupt driven, but prevents you  from running any other code, until the next connection.

 socket_select($changed,$write=NULL,$except=NULL,NULL);

Changing the above line’s timeout settings to 0,250000 (last parameter is in microseconds)  , will cause it to fall through after the time has elapsed, giving you a chance to do some processing, this allows us to do our broadcast ,however this is a poor man’s callback (and as noted below not the best way) .Important DO NOT set the timeout value too short (remember its expressed in microseconds 1.0s=1,000,000 microseconds), if you do this it will cause your while( true) loop to run very quickly and eat up all the CPU cycles of the server machine, making performance very poor.  Read more about socket_select from php’s documentation.

socket_select($changed,$write=NULL,$except=NULL,0,250000

)

;  //250000=0.250 secs

Next let’s write the actual broadcast function, its very straightforward loop through all the clients and for those connected send them the message.

 function broadcast($msg){
 foreach($this->users as $user){ //send to ALL connected users
 if(isset($user->socket)==true ) //confirm there's still a connection
 $this->send($user->socket,$msg);
 } //end foreach
 } //end broadcast

Finally somewhere inside the while(true) main loop of the server  simply call the broadcast function when required, such as when certain events happen or if you want to do a IRC or chatroom style app, just re-broadcast all the messages to all the connected users…

Another reason I left this out is because the method described above is not the best implementation, using something like threads is a better approach and PHP is not most practical language to write servers like this in. I would recommend JAVA. I’ll stress, I’m not trying to make this php webscoket server into a complete production-ready software  with all the bells and whistles but I think this one feature is worth mentioning.

Heartbeat (ping/pong ) frames

Finally, one more thing I noticed is that some browser’s, IE 11 in my case periodically (about every 30s send out a PING frame), ping/pong  aka heartbeat frames are a method that browsers and servers use to check on the status of the connection and may at their discretion terminate the connection if no response occurs . These frames are typically responded to by the server/client to indicate they are still listening,as per the RFC when you receive a ping frame you need to reply in kind with a pong Frame , again for exact details refer to the Ping/Pong frame section of the RFC document. Failure to do say may be an indication that the communication channel is broken and the connection may be ended by the requesting client.

Messaging Protocols like MQTT, STOMP, and WAMP (over websockets)

As I mentioned above the frame_encode and frame_decode are primitive methods of getting the basic textual data in and out of web-socket connections, but really a more evolved messaging setup will be needed as your application becomes more sophisticated. While more websocket traffic today is likely using serialized JSON, websocket messages can take many forms and there are starting to emerge certain API’s and frameworks that support this.

There are well established messaging protocols that offer point-to-point and pub/sub (publisher /subscriber) approaches that will run on top of web sockets, in other words some industrious folks have wrapped all those frame_encode/decode into well established protocols. Here are some of the more popular ones.

  •  The Web Application Messaging Protocol (WAMP) network protocols, these protocols can handle  all varieties of inbound and outbound messaging formats (PubSub and RPC) . This gives you greater ability to communicate with not just your own websocket enabled servers but virtually anyone else. One example is of the AutoBahn  project provides open-source implementations
  • STOMP (simple text-oriented message protocol). STOMP (Simple/Streaming Text Oriented Messaging Protocol) is the only one of these three protocols to be text-based, making it more analogous to HTTP in terms of how it looks under the covers. Like AMQP, STOMP provides a message (or frame) header with properties, and a frame body. The design principles here were to create something simple, and widely-interoperable. For example, it’s possible to connect to a STOMP broker using something as simple as a telnet client.
  •  MQTT (Message Queue Telemetry Transport) started in 1999 , over the past couple of years the protocol has been moved into the open source community, seen significant growth in popularity as mobile applications have taken off, it provides publish-and-subscribe messaging (no queues, in spite of the name) and was specifically designed for resource-constrained devices and low bandwidth, high latency networks such as dial up lines and satellite links, for example Arduino and other IoT devices make use of it for telemtry communications.  Basically, it can be used effectively in embedded systems. Several online services like popular HiveMQ  run MQTT over web sockets

Websocket security and cross domain usage

First off webSockets can do cross domain communication, it follows the (cross-origin sharing)  CORS methodoloy and they are not limited by the SOP (Same Origin Policy),such as traidtional Javascript is, inside of a browser. Because of this Websockets of course have the potential of cross-domain type of attacks, while I’m not going to go into a detailed description on websocket security (which is a subset of modern internet security) , suffice it to say that its up to the server author (user) to ensure and validate the  client origin and ensure that the frames are not being tampered with.

Currently  if you want to restrict the browser from only communicating with the server on the same domain you have that option use your  header policy in the browser Content-Security-Policy header. This will lock down the websocket to the originating domain.

Also for sensitive data communications always use  wss:// (Secure websockets )  , to help ensure a fairly strong level of line-level encryption, again you need a supporting server to handle this..

WebSocket server conclusion

I think by now you’re probably coming to the conclusion why most folks prefer to use a specific websocket server or websocket API/framework library (like node.js + Socket.IO)  for the server component, writing a true production-level communications server is simply a lot of work, and its best to use specialized products . Nonetheless I think once you get this working you can begin to realize the power of Websockets, and how they bring real-time two communications to a whole new level on the web, so long long polling, hello highly efficient bi-directional communication. Expect to start to see them used more heavily as the “real-time” web becomes more prevalent.

Also keep your eye on a corollary browser technology called SSE ( Server-Side Events) , currently still in draft stages today , but it aims to handle many  of the use cases that web-sockets can. Basically where web-sockets is a flexible an bi-directional browser communications technology , SSE are more focused on streaming server-to-client messages (think stock tickers where you just need to stream server data), with a lot less bi-directional requirements. Some of the SSE advantages, they are sent over traditional HTTP (so a lot less complexity there) , they also support automatic re connection, event id’s among st a slew of other features..

That’s the basic’s in a nutshell, there’s a lot more to websockets than what I outlined in this blog post, but download below and try it yourself.

Demo

Because of firewall issues, I can’t run a server here on my web hosts provider, so All this page does is point the client to an open website echo.websocket.org, but you simply modify your client to point to your local server.

Websocket Client page

Websocket Client page

Download & installation

I have simply zipped up the few files that make up this client/server.  Remember to change the server $ip_address on the client and the ws_server.php files to match, to get it to work on your box. Installation steps are as follows:

  • unzip files into a folder on your web server
  • edit the ws_server.php changing the $server_ip address to match that of your server
  • on the client.html edit the default value <input id=”host” type=”textbox” size=”35″ value=”echo.websocket.org”/> to point to your actual server_ip
  • via the command line start-up your php server  ( php -q ws_server.php )
  • open your client.html page, if all goes well you should see the resulting command Welcome- Status 1

I also included  a simple test_socket_server (which IS NOT A web socket server) , but simply sets up a basic socket connection which is useful to confirm that your server is not blocked by firewall or other networking issues. You can talk to it via simple telnet server_ip port. Please feel free to share this link and leave comments if you found this helpful.

46 thoughts on “Websockets : PHP code complete tutorial client & server

  1. Reply iKatalogi Aug 1,2013 11:51 am

    Nice post. I learn something totally new and challenging on sites
    I stumbleupon on a daily basis. It’s always helpful to read content from other writers and practice something from their web sites.

  2. Reply Richard Lopes Oct 13,2013 10:54 pm

    Hi!

    And about the header: Sec-WebSocket-Accept ?

  3. Reply haidadr Jan 29,2014 11:16 am

    i got error on line 109 unexpected ?

  4. Reply joseph Feb 2,2014 1:54 am

    need help getting realtime auction listing goin, can you help?

  5. Reply Anil Mar 21,2014 12:43 pm

    Hello,

    I m a php developer and i want to develop a socket chat with real time request or Like facebook chat window. Can you suggest me any idea or web socket code in php so i can study and get new thing. I will be thankfull to you.

  6. Reply Anand Apr 5,2014 5:49 am

    I am getting this error
    “WebSocket Error [object Event]”. What needs to do?

  7. Reply Anand Apr 7,2014 4:57 am

    I am getting this error
    failed: Error during WebSocket handshake: Unexpected response code: 200
    What needs to do?

  8. Reply Robert Jun 9,2014 5:31 pm

    Was this ever resolved? If yes, how it was resolved as I’m getting the same error? I’ve tried the instructions below but I still get the error.

    Thank you in advance for your help!

  9. Reply tonyb Dec 17,2014 3:21 pm

    I have updaed the posting, its all now fully funcitoning, check out this url:
    http://www.abrandao.com/2013/06/25/websockets-html5-php/

  10. Reply bhudaya Dec 18,2014 6:37 am

    hallo tony…
    would you please tell me how push data from ws server persistencely to many client, without
    trigger from client .

    thanks u

    • Reply Tony B. Dec 18,2014 10:44 am

      Im planning on creating a broadcast() function for this purpose, but have not done that yet, the idea is to alter the socket_select($changed,$write=NULL,$except=NULL,NULL); so it provides a hook (callback) for you to run server_side broadcast code. Because as its written it will wait (not timeout) in socket_select until a new request comes in ,so you need a method to take a look http://php.net/manual/en/function.socket-select.php and special attention to the tvsec and usec timeout parameters. But be careful adding timeout code could introduce subtle bugs. A better approach is using a callback within the socket_select to handle server code..try it and let me know.

  11. Reply Keith T Apr 12,2015 2:35 am

    Been doing php for a long time. This is quite possibly the coolest thing I have seen yet. I was actually looking for a way to ease my pain in getting the handshake to connect without having to go through all the bull yaga in C (port to a php file to do handshake first, then use a C socket). May just use this for now instead. If I do any load tests I will post them where everyone can see them. Well, unless you have already done that.

  12. Reply me email gone Apr 14,2015 12:57 am

    make sure the email is gone from the browser

  13. Reply Sandeep jayan Aug 31,2015 1:34 pm

    hi,
    how to check connection status and reconnect if connection closed.

    thanks

  14. Reply Hemant Sep 9,2015 6:03 am

    Nice explanation Tony B., it works.

    I have establish my server using PHP (Ratchet Server) and replaced server path in Host text box you provided. Its working. Though I am still trying to understand your code 🙂 and update it according to my need.

    Many Thanks for your article.

  15. Reply khaled Jan 22,2016 6:54 pm

    So Good top; thanks for your information thanks

  16. Reply s3m Jan 26,2016 6:58 pm

    any update for the wss server ?

  17. Reply khaled Feb 25,2016 10:30 am

    thanks fot this exmple, tis is so helpfull

  18. Reply Jitesh Aug 22,2016 3:56 am

    make sure the email is gone from the browser

  19. Reply Igor de Paula May 30,2017 4:04 pm

    Very interesting, put it on github for more collaborations and collaborators

  20. Reply Ion Stefanache Oct 29,2017 5:53 am

    when start the server with specific command(php -q ws_server.php)
    In phpwebsocket.php
    line 49: $changed = $this->sockets;
    lin 50: socket_select($changed,$write=NULL,$except=NULL,NULL);
    I received one error in php(5.3.8 version)”
    Strict standards: Only variables should be pased by reference in…\phpwebsocket.php on line 50

  21. Reply Harry Jan 10,2018 4:40 pm

    Great sample. It works perfect. Thanks.

    What is the different beetwen “php server.php” & “php -q server.php” ?
    I tried for both are working good.
    At some other tutorial i found “-f” for “php -f server.php”
    What’s the meaning of “-f” and “-q” ?

    One more question:
    How can we broadcast a message to all user ?

    Thanks a lot.

  22. Reply Harry Jan 11,2018 8:41 pm

    Hi, i just created “function broadcast” and it works for me. But i am not so sure is it a good way or not. The function is to broadcast message to All Users.

    Add the function to file “phpwebsocket.php” :

    function broadcast($messages){
    $n=count($this->users);
    for($i=0;$isend($this->users[$i]->socket,$messages);
    }
    }

    Call the function after handshake :

    $this->broadcast(“USER-” . $user->id . ” JUST JOINED THE SHOW!”);

    Good Luck!

  23. Reply Harry Jan 11,2018 8:48 pm

    Hi, i just created “function broadcast” and it works for me. But i am not so sure is it a good way or not. The function is to broadcast message to All Users.

    Add the function to file “phpwebsocket.php” :


    function broadcast($messages){
    $n=count($this->users);
    for($i=0;$isend($this->users[$i]->socket,$messages);
    }
    }

    Call the function after handshake :


    $this->broadcast("USER-" . $user->id . " JUST JOINED THE SHOW!");

    Good Luck!

  24. Reply Harry Jan 11,2018 8:51 pm

    Sorry, the script is not complete after submit.

    “for($i=0;$i < $n;$i++){"

  25. Reply LJunior Jan 23,2018 6:51 am

    Very good stuff. You could turn into a course covering not only chats but also IoT and make available on Udemy. What do you think? I’m looking for stuff like receiving connections coming from programs like this () but without success … How much would you charge me to give me this support?

  26. Reply Ulta Eyeshadow Feb 4,2018 5:15 pm

    This is the perfect website for anyone who would like to understand this topic.

    You understand so much its almost tough to argue
    with you (not that I actually will need to…HaHa).
    You certainly put a fresh spin on a topic that’s been discussed for ages.
    Great stuff, just wonderful!

  27. Reply akomaoako Jun 14,2018 1:55 am

    hi! i am new to PHP websocket …. would that work in 2 browsers at the same time example i use chrome try sending message to the server would that be also seen in mozilla browser for example ………… and also @harry what do you mean to broadcast using the function you added in phpwebsocket.

  28. Reply Herbert Sep 14,2018 7:48 am

    I can not get data encoding to work. frame_decode(frame_encode(“TestString”)) does not work!

  29. Reply Jag Jul 11,2019 11:41 pm

    Client is getting disconnected while trying wss:// instead of ws://.
    I am running the above example on xampp, configured apache to handle https, thenn changed the ‘ws’ in the client.html to wss.
    Client code –
    host=”wss://10.10.117.2:4444″;
    socket = new WebSocket(host);
    …..
    Following are the messages from the server console.
    PHP WebSocket Server running….
    Server Started : 2019-07-12 06:33:05
    Listening on : 10.10.117.2 port 4444
    Master socket : Resource id #5
    …. awaiting connections …
    in the while loop
    got the socket
    listening…

    Resource id #6 CONNECTED!
    in the while loop
    got the socket
    listening…

    got a child socket.

    Bytes resceivedd 517
    Handshaking (User: 5d280d84c1c42 )

    WS Requesting handshake…
    Issuing websocket Upgrade

    Done handshaking… User count:1
    in the while loop

    got the socket

    listening…

    got a child socket.

    Bytes resceivedd 7
    header[0] =1

    header[1] =0

    header[2] =2

    header[3] =2

    (user: 5d280d84c1c42) msg> G
    > not understood – 06:33:08 (29 bytes)

    in the while loop

    got the socket

    listening…

    Resource id #7 CONNECTED!
    got a child socket.

    DISCONNECTED! User count:1

    • Reply Tony B. Jul 23,2019 8:45 pm

      wss:// may require SSL setup (SSL certificate) , have you tried a self-signed cert?

      • Reply Jag Jul 23,2019 9:12 pm

        Hi
        thank for response, I have created self signed cert and made necessary conf changes in the apache conf files, but no luck. every other page which doesn’t use websockets have loaded successfully.

  30. Reply Glenn Aug 24,2019 4:46 pm

    Hi Tony,

    Excellent post, I stumbled upon this while looking for a way to do websocket connections.

    I think like Jag I just need some pointers on how to set this up over s secure connection.

    I have 2 ip addresses running on Apache one defecated for Apache with port 443 and 80 running. Then my other IP is running the php socket connection on 443 from client to server

    Although I am using wss://:443 in the client.html I am getting an error when connecting seems like I need to use an SSL cert configuration in the phpWebsocket class file but don’t know how I should set this up.

    Do you have any pointers on this. I have googled quite a bit and not really seeing the answer.

    Any help appreciated.

  31. Reply Jag Sep 6,2019 1:16 am

    Hi Glenn,

    Any luck so far? at the moment I am using Ajax to call the backend every 1 sec. As I was busy with other projects I left it for some time. I am again back on this project. Please share if you had any success?

  32. Reply Vincent Nov 8,2022 4:49 pm

    I too, am unable to get this to work using https:. It works with http: and ws: but as soon as I switch it to wss: and load it using http: I get “unable to connect” errors. I have an SSL certificate installed and working, so what’s the trick here?

Leave a Reply to LJunior Cancel Reply