Build Real-Time Chat Application using PHP and Websocket

In this tutorial, we will develop a real-time chat application using PHP and WebSocket. As we have already published tutorial on Chat Application using PHP and Ajax, but we know it’s not the best solution. So here, we will use WebSocket to create a real-time chat application.

Websocket allows to create persistent connection between server and client. In this scenario, there are server side and multiple clients. A client can connect with the server side and communicate with other clients by sending messages. The server will handle message sending to the client.

So let’s proceed with developing real-time chat app.

Create Chatroom Page

First we will create index.html and include Bootstrap and css files.


<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
<link href="style.css" rel="stylesheet">

We will create form to send message and display.

<div class="container mt-4 chat-container">
	<h1>Realtime Chat Room</h1>
	<div class="chat-room mb-3" id="chat-room"></div>
	<form id="chatForm">
		<input type="text" name="user" id="user" class="form-control mb-2" placeholder="Name" required>
		<input type="text" name="message" id="message" class="form-control mb-2" placeholder="Message" required>
		<button type="submit" id="btnSend" class="btn btn-success w-100">Send</button>
	</form>
</div>

Implement WebSocket Server

Now we will create php-socket.php file and implement WebSocket server. We will define host and post to connect with websocket.

<?php
define('HOST_NAME',"localhost"); 
define('PORT',"5000");
$null = NULL;

require_once("class.chathandler.php");
$chatHandler = new ChatHandler();

$socketResource = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
socket_set_option($socketResource, SOL_SOCKET, SO_REUSEADDR, 1);
socket_bind($socketResource, 0, PORT);
socket_listen($socketResource);

$clientSocketArray = array($socketResource);
while (true) {
	$newSocketArray = $clientSocketArray;
	socket_select($newSocketArray, $null, $null, 0, 10);	
	if (in_array($socketResource, $newSocketArray)) {
		$newSocket = socket_accept($socketResource);
		$clientSocketArray[] = $newSocket;		
		$header = socket_read($newSocket, 1024);
		$chatHandler->doHandshake($header, $newSocket, HOST_NAME, PORT);		
		socket_getpeername($newSocket, $client_ip_address);
		$connectionACK = $chatHandler->newConnectionACK($client_ip_address);		
		$chatHandler->send($connectionACK);		
		$newSocketIndex = array_search($socketResource, $newSocketArray);
		unset($newSocketArray[$newSocketIndex]);
	}
	
	foreach ($newSocketArray as $newSocketArrayResource) {	
		while(socket_recv($newSocketArrayResource, $socketData, 1024, 0) >= 1){
			$socketMessage = $chatHandler->unseal($socketData);
			$messageObj = json_decode($socketMessage);			
			$chatMessage = $chatHandler->createChatBoxMessage($messageObj->chat_user, $messageObj->chat_message);
			$chatHandler->send($chatMessage);
			break 2;
		}		
		$socketData = @socket_read($newSocketArrayResource, 1024, PHP_NORMAL_READ);
		if ($socketData === false) { 
			socket_getpeername($newSocketArrayResource, $client_ip_address);
			$connectionACK = $chatHandler->connectionDisconnectACK($client_ip_address);
			$chatHandler->send($connectionACK);
			$newSocketIndex = array_search($newSocketArrayResource, $clientSocketArray);
			unset($clientSocketArray[$newSocketIndex]);			
		}
	}
}
socket_close($socketResource);

Implement Chat Handler

We will create class.chathandler.php and implement chatHandler class. We will create methods to handle connections and message sending.

<?php
class ChatHandler {
	function send($message) {
		global $clientSocketArray;
		$messageLength = strlen($message);
		foreach($clientSocketArray as $clientSocket) {
			@socket_write($clientSocket,$message,$messageLength);
		}
		return true;
	}

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

	function seal($socketData) {
		$b1 = 0x80 | (0x1 & 0x0f);
		$length = strlen($socketData);
		
		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.$socketData;
	}

	function doHandshake($received_header,$client_socket_resource, $host_name, $port) {
		$headers = array();
		$lines = preg_split("/\r\n/", $received_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')));
		$buffer  = "HTTP/1.1 101 Web Socket Protocol Handshake\r\n" .
		"Upgrade: websocket\r\n" .
		"Connection: Upgrade\r\n" .
		"WebSocket-Origin: $host_name\r\n" .
		"WebSocket-Location: ws://$host_name:$port/demo/shout.php\r\n".
		"Sec-WebSocket-Accept:$secAccept\r\n\r\n";
		socket_write($client_socket_resource,$buffer,strlen($buffer));
	}
	
	function newConnectionACK($client_ip_address) {
		$message = 'A new user ' . $client_ip_address.' joined';
		$messageArray = array('message'=>$message,'message_type'=>'chat-connection-ack');
		$ACK = $this->seal(json_encode($messageArray));
		return $ACK;
	}
	
	function connectionDisconnectACK($client_ip_address) {
		$message = 'User ' . $client_ip_address.' leave';
		$messageArray = array('message'=>$message,'message_type'=>'chat-connection-ack');
		$ACK = $this->seal(json_encode($messageArray));
		return $ACK;
	}
	
	function createChatBoxMessage($chat_user,$chat_box_message) {
		$message = ": <div class='chat-room-message'>" . $chat_box_message . "</div>";
		$messageArray = array('user'=>$chat_user,'message'=>$message,'message_type'=>'chat-room-html');
		$chatMessage = $this->seal(json_encode($messageArray));
		return $chatMessage;
	}
}
?>

Handle WebSocket At Client End

We use below JavaScript code to create WebSocket in client side and define callback handlers to handle the different chat events. We will check connection, messages and error.

We will use WebSocket protocol (ws://) to use the address of the WebSocket where WebSocket handshake is handled.


<script>
$(document).ready(function(){
	var websocket = new WebSocket("ws://localhost:5000/php-realtime-chat-using-websocket/php-socket.php"); 
	websocket.onopen = function(event) { 
		displayMessage("<div class='text-success-message'>Connection is established!</div>");
	};
	websocket.onmessage = function(event) {
		var messageData = JSON.parse(event.data);            
		let message = messageData.message;
		if(messageData.user) {
			message = messageData.user+" "+message;
		}
		let style = '';
		if(messageData.user && messageData.user == $('#user').val()) {
			style = 'text-align:right;';
		} else {
			style = 'color:orange;';
		}
		displayMessage("<div class='chat-message' style='"+style+"'>" + message + "</div>");
		$('#message').val('');
	};
	websocket.onerror = function(event){
		displayMessage("<div class='text-danger'>Problem due to some Error</div>");
	};
	websocket.onclose = function(event){
		displayMessage("<div class='text-warning'>Connection Closed</div>");
	};
	$('#chatForm').on("submit", function(event){
		event.preventDefault();
		var messageData = {
			chat_user: $('#user').val(),
			chat_message: $('#message').val()
		};
		websocket.send(JSON.stringify(messageData));
	});
});

function displayMessage(messageHTML) {
	$('#chat-room').append(messageHTML);
}
</script>

Start WebSocket Server

We will run start WebSocket server by running php-socket.php from command line. Just move to the project directory and run below command.

php -q php-socket.php

After running above, load http://localhost/php-realtime-chat-using-websocket/index.php in multiple browsers to start chat.

Download

Leave a Reply

Your email address will not be published. Required fields are marked *