Skip to content

Instantly share code, notes, and snippets.

@teocci
Created July 1, 2016 07:45
Show Gist options
  • Save teocci/5f4230fb5314129baefce4d33c86f7df to your computer and use it in GitHub Desktop.
Save teocci/5f4230fb5314129baefce4d33c86f7df to your computer and use it in GitHub Desktop.

Simple Chat Using PHP Socket + HTML5 WebSocket

HTML5's Websocket provides an important feature for establishing a socket connections between web browsers and servers. Once the connection has been established with the server, all the messages are sent directly over a socket rather than usual HTTP response and requests, giving us much faster and persistent communication between a web browser and a server. In this tutorial, let’s create a simple chat system using this technology.

Chat Server using PHP Socket: The Implementation

First, we need to create a Socket server that runs permanently, performs the handshaking, send/receive data from the chat page, and finally handles multiple clients. To this end, we will create a daemon script in PHP. I know what you might be thinking, PHP is mostly used to create dynamic webpages, BUT we can also create background daemons using nothing but PHP. How we do that? Well just follow this steps:

1. Install WebSocket Server

After finished with the Installation of a local web server such as LAMP, WAMP or XAMPP. For this tutorial, I am using a LAMP environment.

2. PHP Socket

Basically our PHP Chat server should do the following. - Open a socket and bind it to a particular address and port. - Listen incoming connections. - Accept connections and Handshake with clients. - Unmask/Encode data frames.

Open a socket:

First, we create a socket with the PHP fuction socket_create(Domain, Type, Protocol) like this:

$host = '127.0.0.1';	//host
$port = '8080'; 			//port

//Create TCP/IP stream socket and return the socket resource
$socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
//reusable port
socket_set_option($socket, SOL_SOCKET, SO_REUSEADDR, 1);

Bind to a address and port:

Bind the address to the socket. Once the socket is created, we want our server to listen to incoming connection on that socket. this has to be done before a connection is established using socket_connect().

// Bind the source address
socket_bind($socket, 'localhost');

// Listen to incoming connection
socket_listen($socket);

// Accept new connections
socket_accept($socket);

Handshake:

Client has to introduce itself by sending a handshake request to establish a successful connection with server, a handshake request contains a Sec-WebSocket-Key a base64 encoded randomly generated 16-byte value. And the server reads the key, attaches magic string “258EAFA5-E914-47DA-95CA-C5AB0DC85B11”, hash key with SHA1, returns the key in Sec-WebSocket-Accept encoded with base64.

Handshake response with PHP :

// Performs the handshake with a 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  = "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/chatsocket/server.php\r\n".
	"Sec-WebSocket-Accept:$secAccept\r\n\r\n";
	socket_write($client_conn,$upgrade,strlen($upgrade));
}

Notice | The WebSocket handshake is designed to be HTTP compatible. TCP protocols often use \r\n (carriage return + line feed) to indicate a new line. \r\n\r\n is used by HTTP to indicate the end of the handshake. Here you can find a more advanced handshaker.

Unmasking/Encoding data frames.

After the handshaking, clients can send and receive messages. However, the sent messages are all encrypted. If we want to display them, each data frame needs to be unmasked following these specifications described here.

//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;
}

WebSocket Chat

1. Browser Support

Old browsers do not support WebSockets. Thefore, you need to find-out all WebSocket supported browsers in here.

The client side WebSocket implementation is very easy, the entire code consist of few methods and events. Let's create a js directory and a new empty file named socket.js:

mkdir js
touch js/socket.js

The main structure of our WebSocket implementation in the client will be as follows:

//create a new WebSocket object.
websocket = new WebSocket("ws://host:port/path-to-the-app/daemon.php");
websocket.onopen = function(evt) { /* do stuff */ }; //on open event
websocket.onclose = function(evt) { /* do stuff */ }; //on close event
websocket.onmessage = function(evt) { /* do stuff */ }; //on message event
websocket.onerror = function(evt) { /* do stuff */ }; //on error event
websocket.send(message); //send method
websocket.close(); //close method

To open the socket connection, we simply call new WebSocket("ws://host:port/path-to-the-app/daemon.php");, since WebSocket uses a different protocol for the connections, we use ws:// instead of http://, followed by host, port number, path to our app, and the daemon script in your server.

Right after opening the connection, we need to attach some event handlers that let us know status of connectivity, errors and incoming messages, for your references:

WebSocket(wsUri) — creates a new WebSocket object.
.onopen — Event occurs when connection is established.
.onclose — Event occurs when connection is closed.
.onmessage — Event occurs when client receives data from server.
.onerror — Event occurs when there is an error.
.send(message) — Transmits data to server using open connection.
.close() — Terminates existing connection.

For out example we will implement the WebSocket with the jQuery support following this structure:

$(document).ready(function(){
    //Open a WebSocket connection.
    var wsUri = "ws://localhost:9000/daemon.php";
    websocket = new WebSocket(wsUri);

    //Connected to server
    websocket.onopen = function(ev) {
        alert('Connected to server ');
    }

    //Connection close
    websocket.onclose = function(ev) {
        alert('Disconnected');
    };

    //Message Receved
    websocket.onmessage = function(ev) {
        alert('Message '+ev.data);
    };

    //Error
    websocket.onerror = function(ev) {
        alert('Error '+ev.data);
    };

     //Send a Message
    $('#send').click(function(){
        var mymessage = 'This is a test message';
        websocket.send(mymessage);
    });
});

Now open your favorite editor and have a look at code below for example:

$(document).ready(function () {
    //create a new WebSocket object.
    var wsUri = "ws://127.0.0.1:8080/chatsocket/server.php";
    websocket = new WebSocket(wsUri);

    websocket.onopen = function (ev) { // connection is open 
        $('#message_box').append("<div class=\"system_msg\">Connected!</div>"); //notify user
    };
    
    $('#message').keypress(function (e) {
        var key = e.which;
        if (key == 13)  // the enter key code
        {
            $('#send-btn').click();
            return false;
        }
    });

    $('#send-btn').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,
            name: myname,
            color: '<?php echo $colours[$user_colour]; ?>'
        };
        //convert and send data to server
        websocket.send(JSON.stringify(msg));
    });

    //#### Message received from server?
    websocket.onmessage = function (ev) {
        var msg = JSON.parse(ev.data); //PHP sends Json data
        var type = msg.type; //message type
        var umsg = msg.message; //message text
        var uname = msg.name; //user name
        var ucolor = msg.color; //color

        if (type == 'usermsg')
        {
            $('#message_box').append("<div><span class=\"user_name\" style=\"color:#" + ucolor + "\">" + uname + "</span> : <span class=\"user_message\">" + umsg + "</span></div>");
        }
        if (type == 'system')
        {
            $('#message_box').append("<div class=\"system_msg\">" + umsg + "</div>");
        }

        $('#message').val(''); //reset text
    };

    websocket.onerror = function (ev) {
        $('#message_box').append("<div class=\"system_error\">Error Occurred - " + ev.data + "</div>");
    };
    websocket.onclose = function (ev) {
        $('#message_box').append("<div class=\"system_msg\">Connection Closed</div>");
    };
});

2. HTML5: The Chat Page

As explained in examples above, we start by creating a WebSocket object, attaching event handlers and then using websocket.send() method to send the data. Since we are sending a collection of chat values, such as user name, message, color etc, we’ll convert our data to JSON format before sending to server.

Below is our HTML for the chat system, it comes with some basic styling, I have not included the CSS part here, but you should find it in sample file.

<div class="chat_wrapper">
<div class="message_box" id="message_box"></div>
<div class="panel">
<input type="text" name="name" id="name" placeholder="Your Name" maxlength="10" style="width:20%"  />
<input type="text" name="message" id="message" placeholder="Message" maxlength="80" style="width:60%" />
<button id="send-btn">Send</button>
</div>
</div>

3. Starting Chat Server

Finally, it is time to run our chat server by typing this command:

php -q /path-to-server/server.php

Once all of this is done, we can navigate to the http://your-host/path-to-the-app/ page using your browser and start test chatting.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment