In this tutorial, we are going to create a simple chat application using WebSocket and PHP socket programming. The WebSocket is used to create a bridge to send or receive messages from the PHP chat server. In the web world, we generally use HTTP request methods to communicate between the client and server side. In this chat example, we use sockets to communicate with the server.
For establishing a socket connection between the client and the server, we use the WebSocket protocol (ws://) to specify the address of the PHP page where the WebSocket handshake is handled. After creating WebSocket there are callbacks to handle the events that occur between the client and the server during the chat process.
Creating WebSocket and Callback Event Handlers
The following script is used to create WebSocket in client side and define callback handlers to handle the different chat events. These handlers give acknowledgments about the connection state, chat messages and the errors if any. The chat message is encoded in JSON format and sent to the server on submit.<script> function showMessage(messageHTML) { $('#chat-box').append(messageHTML); } $(document).ready(function(){ var websocket = new WebSocket("ws://localhost:8090/demo/php-socket.php"); websocket.onopen = function(event) { showMessage("<div class='chat-connection-ack'>Connection is established!</div>"); } websocket.onmessage = function(event) { var Data = JSON.parse(event.data); showMessage("<div class='"+Data.message_type+"'>"+Data.message+"</div>"); $('#chat-message').val(''); }; websocket.onerror = function(event){ showMessage("<div class='error'>Problem due to some Error</div>"); }; websocket.onclose = function(event){ showMessage("<div class='chat-connection-ack'>Connection Closed</div>"); }; $('#frmChat').on("submit",function(event){ event.preventDefault(); $('#chat-user').attr("type","hidden"); var messageJSON = { chat_user: $('#chat-user').val(), chat_message: $('#chat-message').val() }; websocket.send(JSON.stringify(messageJSON)); }); }); </script>
PHP Socket Programming for the Chat Application
This PHP code checks for the new socket connection request. If any of a new connection request is found, then it will accept and perform the handshake with the new socket resource. Then, it sends an acknowledgment to the client about the connectivity by sealing the encoded acknowledgment message.It receives the socket data sent via the existing connections and unseals and decodes it to bundle the received data and send it to the chat client. The handshake, seal, unseal, send functionalities are handled by using the ChatHandler class.
<?php define('HOST_NAME',"localhost"); define('PORT',"8090"); $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); $chat_box_message = $chatHandler->createChatBoxMessage($messageObj->chat_user, $messageObj->chat_message); $chatHandler->send($chat_box_message); 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); ?>and the chat handler class is
<?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 = 'New client ' . $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 = 'Client ' . $client_ip_address.' disconnected'; $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 = $chat_user . ": <div class='chat-box-message'>" . $chat_box_message . "</div>"; $messageArray = array('message'=>$message,'message_type'=>'chat-box-html'); $chatMessage = $this->seal(json_encode($messageArray)); return $chatMessage; } } ?>
0 comments:
Post a Comment