Chat Implementation Using HTML5 WebSocktes & PHP

  • Post published:August 18, 2015

WebSocket is a feature of HTML5 for establishing socket connections between a web browser and a Server. Once connection has been established with the server, all WebSocket data (frames) are sent directly over a socket rather than usual HTTP response and requests, giving us a much faster and persistent communication between a web browser and a server.

Implementation:

Server: Server is a daemon (that runs in background) using nothing but PHP. In server file, we create a PHP socket which states listening to the clients on a specified port. Server accepts incoming Clients and map their socket ID’s with a unique Client ID in order to send/receive data to and from a specific client.

Client: Client creates a web socket request to server domain and port. After handshake, a persistent connection is established. Client can receive anything sent from the server on real time basis, no need to further request server for updates.

Advantages:

  • Instant data passing between server and clients
  • Server can push data to any client
  • Faster communication between server and clients
  • No server overloading with Ajax
    .

Sample Code:

At Server Side:

Command to run Server file is as follows:

php -q \path-to-server\server.php
$host = 'cr/chat'; //host
$port = '9001'; //port
$null = NULL; //null var

//Create TCP/IP sream socket
$socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
//reuseable port
socket_set_option($socket, SOL_SOCKET, SO_REUSEADDR, 1);

//bind socket to specified host
socket_bind($socket, 0, $port);

//listen to port
socket_listen($socket);
$cc=array();
//create & add listning socket to the list
$clients = array($socket);

//start endless loop, so that our script doesn't stop
while (true) {
      //manage multipal connections<br>      $changed = $clients;
      //returns the socket resources in $changed array
      socket_select($changed, $null, $null, 0, 10);

      //check for new socket
      if (in_array($socket, $changed)) {
             $socket_new = socket_accept($socket); //accpet new socket
             $clients[] = $socket_new; //add socket to client array

             $header = socket_read($socket_new, 1024); //read data sent by the socket
             perform_handshaking($header, $socket_new, $host, $port); //perform websocket handshake

             socket_getpeername($socket_new, $ip); //get ip address of connected socket

             //make room for new socket
             $found_socket = array_search($socket, $changed);
             unset($changed[$found_socket]);
      }

      //loop through all connected sockets
      foreach ($changed as $changed_socket) {

             //check for any incomming data
             while(socket_recv($changed_socket, $buf, 1024, 0) >= 1)
             {
                   $received_text = unmask($buf); //unmask data
                   $tst_msg = json_decode($received_text); //json decode
                   if($tst_msg->type=='system')
                   {
                          $cc[$tst_msg->id]=array();
                          $cc[$tst_msg->id]['name']=$tst_msg->name;
                          $cc[$tst_msg->id]['soc_id']=$changed_socket;
                          $response_text =mask(json_encode(array('type'=>'system', 'message'=>'Initial message received')));
                          send_message($response_text,$changed_socket); //send data
                   }
                   else
                   {
                   $user_name = $tst_msg->name; //sender name
                   $user_message = $tst_msg->message; //message text
                   $user_color = $tst_msg->color; //color
                   if(isset($cc[$tst_msg->to_id]))
                           {
                           $to_sock=$cc[$tst_msg->to_id]['soc_id'];
                           //prepare data to be sent to client
                           $response_text = mask(json_encode(array('type'=>'usermsg', 'name'=>$user_name, 'message'=>$user_message, 'color'=>$user_color)));
                           send_message($response_text,$to_sock); //send data
                           }
                   }
                   break 2; //exist this loop
             }
             $buf = @socket_read($changed_socket, 1024, PHP_NORMAL_READ);
             if ($buf === false) { // check disconnected client
                   // remove client for $clients array
                   $found_socket = array_search($changed_socket, $clients);
                   socket_getpeername($changed_socket, $ip);
                   unset($clients[$found_socket]);
                   //notify all users about disconnected connection
                   $response = mask(json_encode(array('type'=>'system', 'message'=>$ip.' disconnected')));
                   //send_message($response);
             }
      }
}
// close the listening socket
socket_close($sock);

function send_message($msg,$client_socket)
{

             @socket_write($client_socket,$msg,strlen($msg));
      return true;
}


//Unmask incoming framed message
function unmask($text) {
      $length = ord($text[1]) & 127;
      if($length == 126) {
             $masks = substr($text, 4, 4);
             $data = substr($text, 8);
      }
      elseif($length == 127) {
             $masks = substr($text, 10, 4);
             $data = substr($text, 14);
      }
      else {
             $masks = substr($text, 2, 4);
             $data = substr($text, 6);
      }
      $text = "";
      for ($i = 0; $i < strlen($data); ++$i) {
             $text .= $data[$i] ^ $masks[$i%4];
      }
      return $text;
}

//Encode message for transfer to client.
function mask($text)
{
      $b1 = 0x80 | (0x1 & 0x0f);
      $length = strlen($text);

      if($length <= 125)
             $header = pack('CC', $b1, $length);
      elseif($length > 125 && $length < 65536)
             $header = pack('CCn', $b1, 126, $length);
      elseif($length >= 65536)
             $header = pack('CCNN', $b1, 127, $length);
      return $header.$text;
}

//handshake new client.
function perform_handshaking($receved_header,$client_conn, $host, $port)
{
      $headers = array();
      $lines = preg_split("/\r\n/", $receved_header);
      foreach($lines as $line)
      {
             $line = chop($line);
             if(preg_match('/\A(\S+): (.*)\z/', $line, $matches))
             {
                    $headers[$matches[1]] = $matches[2];
             }
      }

      $secKey = $headers['Sec-WebSocket-Key'];
      $secAccept = base64_encode(pack('H*', sha1($secKey . '258EAFA5-E914-47DA-95CA-C5AB0DC85B11')));
      //hand shaking header
      $upgrade&nbsp; = "HTTP/1.1 101 Web Socket Protocol Handshake\r\n" .
      "Upgrade: websocket\r\n" .
      "Connection: Upgrade\r\n" .
      "WebSocket-Origin: $host\r\n" .
      "WebSocket-Location: ws://$host:$port/server.php\r\n".
      "Sec-WebSocket-Accept:$secAccept\r\n\r\n";
      socket_write($client_conn,$upgrade,strlen($upgrade));
}
 At Client Side:

Command to run at client side is as follows:

$(document).ready(function(){
     //create a new WebSocket object.
     var wsUri = "ws://cr:9001/chat/server.php:9000";
     websoc2.Client Side
ket = new WebSocket(wsUri);

     websocket.onopen = function(ev) { // connection is open
          $('#message_box').append("<div class=\"system_msg\">Connected!</div>"); //notify user
          //prepare json data
          var msg = {
          type:'system',
          id: 7,
          projectId:2424,
          na2.Client Side
me:'mks'
          };
          //convert and send data to server
          websocket.send(JSON.stringify(msg));
     }

     $('#send-b2.Client Side
tn').click(function(){ //use clicks message send button
          var mymessage = $('#message').val(); //get message text
          var myname = $('#name').val(); //get user name

          if(myname == ""){ //empty name?
               alert("Enter your Name please!");
               return;
          }
          if(mymessage == ""){ //emtpy message?
               alert("Enter Some message Please!");
               return;
          }

          //prepare json data
          var msg = {
          message: mymessage,
          type:'user',
          name: 'mks',
          id: 7,
          projectId:2424,
          to_id:5,
          color : '<?php echo $colours[$user_colour]; ?>'
          };
          //convert and send data to server
          websocket.send(JSON.stringify(msg));
          $('#message_box').append("<div><span class=\"user_name\" style=\"color:#F00\">"+myname+"</span> : <span class=\"user_message\">"+mymessage+"</span></div>");
     });