158

PHP WebSocket Chat Application 2.0

Posted February 15th, 2012 (Updated 29 Oct 2013) in Javascript, PHP

Even though it hasn’t worked for quite some time now, my previous PHP WebSocket chat application has garnered quite a bit of attention and is still one of my most heavily trafficked posts. As a result I thought I’d provide you all with a working script as of Feb 15, 2012. Because I’m your typical lazy developer, I’ll be building on top of other peoples’ work – most notably PHPWebSocket.

You can download the final script here. According to the Wikipedia article on WebSockets, it should work in IE10+, Firefox 7+ and Chrome 14+. Personally I tested with Chrome 17.0.963.46 and Firefox 10.0.1.

I want to stress that this tutorial is designed to be extremely basic and as such does not give alot of functionality out of the box (but provides all the tools necessary to add more). It’s simply a working example of a PHP-based WebSocket server.

 

The Implimentation – Server Side

PHP WebSocket Server

PHP WebSocket Server

While the PHPWebSocket class works, it puts alot of variables and constants into the global scope. This is a big no-no in my opinion so I moved the entire script into a class PHPWebSocket and added the following functions:

  • bind: Binds a given function name to a given event. Valid events include:
    • open: Triggers as a user connects
    • close: Triggers as a user disconnects
    • message: Triggers when a user sends data to the server
  • unbind: Unbinds all functions from a given event, or all functions from all events if no event name is given.
  • log: Logs a message to the standard output

Below is an example of your server script for a very basic chat application (Note that I’ve set the server to run on 127.0.0.1. Set this to your network IP if you want others on the network/internet to be able to connect to your server):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
<?php
// prevent the server from timing out
set_time_limit(0);
 
// include the web sockets server script (the server is started at the far bottom of this file)
require 'class.PHPWebSocket.php';
 
// when a client sends data to the server
function wsOnMessage($clientID, $message, $messageLength, $binary) {
	global $Server;
	$ip = long2ip( $Server->wsClients[$clientID][6] );
 
	// check if message length is 0
	if ($messageLength == 0) {
		$Server->wsClose($clientID);
		return;
	}
 
	//Send the message to everyone but the person who said it
	foreach ( $Server->wsClients as $id => $client )
		if ( $id != $clientID )
			$Server->wsSend($id, "Visitor $clientID ($ip) said "$message"");
}
 
// when a client connects
function wsOnOpen($clientID)
{
	global $Server;
	$ip = long2ip( $Server->wsClients[$clientID][6] );
 
	$Server->log( "$ip ($clientID) has connected." );
 
	//Send a join notice to everyone but the person who joined
	foreach ( $Server->wsClients as $id => $client )
		if ( $id != $clientID )
			$Server->wsSend($id, "Visitor $clientID ($ip) has joined the room.");
}
 
// when a client closes or lost connection
function wsOnClose($clientID, $status) {
	global $Server;
	$ip = long2ip( $Server->wsClients[$clientID][6] );
 
	$Server->log( "$ip ($clientID) has disconnected." );
 
	//Send a user left notice to everyone in the room
	foreach ( $Server->wsClients as $id => $client )
		$Server->wsSend($id, "Visitor $clientID ($ip) has left the room.");
}
 
// start the server
$Server = new PHPWebSocket();
$Server->bind('message', 'wsOnMessage');
$Server->bind('open', 'wsOnOpen');
$Server->bind('close', 'wsOnClose');
// for other computers to connect, you will probably need to change this to your LAN IP or external IP,
// alternatively use: gethostbyaddr(gethostbyname($_SERVER['SERVER_NAME']))
$Server->wsStartServer('127.0.0.1', 9300);
 
?>

 

The Implimentation – Client Side

WebSocket Client

Very basic WebSocket Client

I wasn’t impressed with the JavaScript class provided with PHPWebSocket either, so instead I’m using a more flexible one called FancyWebSocket (which I’ve also made modifications to). Like the server functions I added, FancyWebSocket allows you to bind functions to open, close, and message events so your page should look something like the following:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
<!doctype html>
<html>
<head>
	<meta charset='UTF-8' />
	<style>
		input, textarea {border:1px solid #CCC;margin:0px;padding:0px}
 
		#body {max-width:800px;margin:auto}
		#log {width:100%;height:400px}
		#message {width:100%;line-height:20px}
	</style>
	<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js"></script>
	<script src="fancywebsocket.js"></script>
	<script>
		var Server;
 
		function log( text ) {
			$log = $('#log');
			//Add text to log
			$log.append(($log.val()?"n":'')+text);
			//Autoscroll
			$log[0].scrollTop = $log[0].scrollHeight - $log[0].clientHeight;
		}
 
		function send( text ) {
			Server.send( 'message', text );
		}
 
		$(document).ready(function() {
			log('Connecting...');
			Server = new FancyWebSocket('ws://127.0.0.1:9300');
 
			$('#message').keypress(function(e) {
				if ( e.keyCode == 13 && this.value ) {
					log( 'You: ' + this.value );
					send( this.value );
					$(this).val('');
				}
			});
 
			//Let the user know we're connected
			Server.bind('open', function() {
				log( "Connected." );
			});
 
			//OH NOES! Disconnection occurred.
			Server.bind('close', function( data ) {
				log( "Disconnected." );
			});
 
			//Log any messages sent from server
			Server.bind('message', function( payload ) {
				log( payload );
			});
 
			Server.connect();
		});
	</script>
</head>
 
<body>
	<div id='body'>
		<textarea id='log' name='log' readonly='readonly'></textarea><br/>
		<input type='text' id='message' name='message' />
	</div>
</body>
 
</html>

 

How to Run It

After downloading, simply open a terminal and type

php5 ./server.php

Then load it up in your browser.

 

Further Reading

If you want to see the true awesomeness of WebSockets and what they can do for you and your site, head on over to Kaazing and check out their Lightning Fast Stock Trade demo. It’s very cool stuff.

A redditor whipped up a version 13 compatible websocket implimentation here. The comments include a couple of links to other implementations.

I would also highly advise you check out Ratchet. It’s an up to date websocket implementation for PHP.

  • Pat

    How would you make it so they could change their username?

    • Joe

      You be a programmer and you write that functionality.

  • luntegg

    Thanks! It’s all work!

  • Daniel

    Thanks. I want to create a chat application. Could I use the server side in php, and customice my own client side in java with sockets?
    My application is java, and I want the php server because I dont have a VPS.
    Thanks.

  • Sebastian

    Hi everyone,

    everythink work’s fine. Thank you so much for this example running out-of-the-box.

    Actually I’m trying to write a message to the socket from another PHP-Script.
    I started with normal PHP-Sockets:

    $string = "Hello World";
    $socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
    $conn = socket_connect($socket, $host, $port);
    $send = socket_write($socket, $string, strlen($string));
    socket_close($socket);

    But here’s the problem: The server does not recognize the new client and can’t send the message to other clients.
    After round about 15 seconds the server get’s the closing information from the client an print this to the console.

    Has anyone an advice, how to write correct at the socket, so all other clients would receive the message from the server?

    Thanks,
    Sebastian

  • Masoud

    Hi,
    How can I run server.php on an online server. I mean, I uploaded the files into my host , but I cant run server.php :(
    Any help

    • Stefan

      Run “php server.php” using ssh

  • Stefan

    Thank you! That’s the only working version of a php websocket server I found :)

  • Fab

    Hi.
    Thank you for offering this great example – helped me a lot !
    I want to add some Authentication (and later authorization). Any idea how to handle authentication by create the connection?

  • Cam

    Thanks a lot. As Stefan said this is the only one that actually works. Cheers Man.

  • Steve Warburton

    I have set this up in myCpanel using :

    wget http://www.wilsea.com/websockets4/server.php

    The ISP has opened port 8000

    I have changed in server.php to

    $Server->wsStartServer(’188.64.188.21′, 8000);

    & index.html to :

    Server = new FancyWebSocket(‘ws://188.64.188.21:8000′);

    In the error log I get:

    [14-Aug-2013 14:25:02 Europe/London] PHP Warning: socket_bind() [function.socket-bind]: unable to bind address [98]: Address already in use in /home/wilseayd/public_html/websockets4/class.PHPWebSocket.php on line 110

    When I run the index/html nothing happens.

    What am I missing here please ?

    • flynsarmy

      You’re already using port 8000 for something. Try a different port.

  • Steve Warburton

    Hi Thanks for the response.

    I goto my ISP to open some more ports. I am now using port 8010.

    The sever runs okay but I get the following :

    WebSocket – status 0
    We are now connected to websocket server. readyState = 1
    [ + ] Received: Hello. Welcome to the Websocket server. Type help to see what commands are available.

    So I send help

    Sent : help
    [ + ] Received: Following commands are available – date, hi
    Disconnected – status 3

    Any ideas ?

    Cheers

    SteveW

  • http://www.cassiolacerda.com.br/ Cássio Lacerda

    Amazing script!!!!!!!!!!

  • Aditi

    Hi,

    Great tutorial.. It seems to work fine for me on local. It even worked for me when I used two systems(clients) in the same network when I changed the server.php to my own lan ip(internal ip). But when I tried clients on different networks it is not connecting to my server.. It says cannot connect to so and so ip.
    This is the error shown in firefox:
    Firefox can’t establish a connection to the server at ws://(my lan ip):9300/.
    this.conn = new WebSocket(url);

    The same problem is arising when I host my code on amazon server.Please help. I am stuck here and I need to submit this ASAP.

    • Arjan

      127.0.0.1 is an internal ipadress, not reachable from the outside.

      Either you have to forward ports from your external ip to your internal ip, or you will have the use the external ip in both client and server.

  • Bjorn van de Peut

    Hey everyone. Just wanted to let you know that we’re currently working on expanding this script to a full blown functional chatsystem. Once we’re done we’ll release the source to you guys ofcourse :)

    What you might find interesting to know is that we devided everything into managable classes. This includes a commands class which will allow you to create your own commands very easily.

    Progress:

    Tabs (for private chats): 98%
    They work. There’s just a tiny bug in it we still have to fix.

    Tabs (for different chatrooms): 0%
    We’re basing everything on 1 socket so this is a tricky one we’re still trying to figure out. We have some ideas but we’re facing challanges like updating the userlist when a different tab is set to active.

    Admin functions kick and ban: 75%
    Kick is fully functional. Ban is build in but not working yet. This is an easy one though as it will function exactly the same as the kick command only updating a mysql table to set the ip-ban.

    Userlist / Chatroomlist: 50%
    The userlist is fully functional. This includes right-click menu based on user rights. Chatroomlist is not there yet as we will work on this when we get to the tabs for chatrooms.

    Smilies: 100%
    We have a smileys button that will open up a div and put the corresponding bblike code in the textfield.

    Logs: 90%
    We have a fully functional log system but for the moment it only logs towards the console. First we thought about logging to mysql but that would undermine the entire idea of using a socket instead of polling. We plan to make it write out txt files instead.

    Profiles: 0%
    Once we’ve got the basic functions fully functional, we’re going to create profiles that will extend the possibilities enormously. Stuff like username color in the userlists based on gender. Locking / Openingen chatboxes based on age / sexuality etc.

    I will post replies to this comment as our progress continues. If you want to help out there’re several things you can do:

    - Reply with (functionallity!) ideas to build in.
    - Be patient! We’re only working with the 2 of us.
    - Stand by to help beta-testing when we’re ready.
    - Coding*

    * If you’re serious about helping us with coding than we’ll require the following from you:

    - Good!!! knowledge of PHP and Javascript (jQuery).
    - Some knowledge of HTML and CSS.
    - Be friendly! If there’s anything we hate than it’s idiots jumping on their feet because the code isn’t pure perfection.
    - Be patient! Again, we’re just with the 2 of us and do have a life besides this project. We won’t be working on this every single day!
    - Preferably a form of commication like Skype as we like to be able to talk to eachother while working without having to type all the time.
    - We will also require some proof of skill. This mainly to prevent ppl applying just to get their hands on the code before it’s ready.

    • flynsarmy

      Sounds awesome :) Keep us informed

    • Bjorn van de Peut

      Just a small update:

      Tabs (for private chats): 100%
      They work as expected now. Beleave it or not. It turned out we accidently used a unique id twice what caused the bug :))

      Admin functions kick and ban: 100%
      They both work now.

      We’re currently working on user registration so you guys can take a look at the progress whenever you feel up for it. I have to figure out how to set up a proper cron job first though so I can let the socket run even when my own computer is turned off.

    • Bjorn van de Peut

      Ok, time for a new update. To keep it a bit clean I will short list everything again so you don’t have to read through all my messages.

      Tabs (for private chats): 100%
      Tabs (for different chatrooms): 100%
      Admin functions kick and ban: 100%
      Userlist / Chatroomlist: 100%
      Smilies: 100%
      Logs: 90%
      Profiles: 0%

      As you can see we’ve made quite some progress the last couple of days. Multiple chatrooms (up to 999) work perfectly and are extremely easy to add / remove. Just add / remove one in a mysql database and they’re added live to the chat. Don’t even need to restart the socket for them. You can even edit them while they’re running. As a little extra feature we’ve added a level to the chatboxes so you can deside which level a user needs in order to be able to access that chatbox.

      We’ve made it so that the system detects if at least 1 chatbox is open. So when a user tries to close the last tab he gets a warning that it’s impossible. Unless ofcourse they close the chat completely. This excludes private chats.

      As for the design we made it so the tabcolor changes when new data appears on a tab that isn’t currently active. It also plays a sound which can be deactivated with a simple checkbox.

      Next we’re going to work on the ability to block private chats like some sort of Do Not Disturb system. After that we’re going to work on the ability to ignore other users. We’ll be doing this with an array. The downside to this is that a user will only be ignored for that session. We could do this with the database instead. But that would mean the database needs to be pulled on each message to see if the user is ignored or not. Which is not vaiable cuz we’d end up on polling again. Maybe we could mix this up so the database is only pulled on login which creates the array to use afterwards and only pull the database more when a new user is added or removed. But that’s just thinking out loud.

      We also think about adding textcolors just for the fun of it. Once those options are done I think it’s time to start working on an Admin control panel so it’s no longer needed to directly work with the database. After that it’s time to finish the log system and start working on the profiles. Once those are done we’ll be ready to start beta testing and we sure would love to see you guys come and help out there :)

    • Bjorn van de Peut

      Well we’ve stumbled upon a bug we can’t figure out. At least not for the moment. It turns out this socketchat can’t handle non UTF-8 characters. A javascript error occurs when a chatter sends one of these characters causing everyone connected to the chat to be disconnected instantly.

      Flyn, perhaps you have some idea how to either fix this or somehow catch this error?

      • flynsarmy

        Pretty busy at the moment, no time to help debug issues. You have a couple of options though.
        1. Use JS to remove (replace with ascii equivalent) utf-8 chars before send
        2. Start debugging at message received on the server side and take it one step at a time. Perhaps filter them out before sending off to other people in the room.

        • Bjorn van de Peut

          Took some time but I managed to fix it. Using js escape when sending msg to server. Than php rawurldecode on the server to prevent issues with commands. Finnally js unescape on msg received to display special characters as expected. Thanks for you advise.

  • yo

    seems I get syntax errror

    root@Yog-Ub-Server:/home/yog# php5 ./server.php
    PHP Parse error: syntax error, unexpected T_VARIABLE in /home/yog/server.php on line 21

    Line 21 seems to be

    $Server->wsSend($id, “Visitor $clientID ($ip) said “$message”");

    Any ideas?

    • flynsarmy

      Have you modified the script at all? Line 21 is fine.

  • Ayps

    Hi Flynsarmy,

    Your script really helpful for me. Thanks!

    But socket creation doesn’t work with external IP.

    Kindly help.

  • Pedro Góes

    Amazing, thank you!

  • sergiu

    Hi, first I’d like you thank you for this great post!!
    I am strugling to make every user choose a username but can’t put it in user’s details, wsClients array… can’t even find the place where to put any code to do that…
    any help appreciated

  • Bjorn van de Peut

    Ok. First of all my appologise for being away for so long not letting anything know. Unfortunately our lives changed quite a bit and we were no longer able to continue our project to turn this chat script into a fully functional chatsystem.

    However, I’d hate to see all our work go to waste and therefor would like to ask if someone is interested in continuing the project. I only ask for the following in return:

    - You must have proven development skills (mainly just to prevent ppl using unfinished code that will end in disaster one way or another).
    - You must release the code for free when it’s finished! As this was our intention before we started this whole project in the first place.

    - You must understand that all our work will be provided as is. You may ask my help from time to time but I won’t be there a lot. Basicly your knowledge of coding should be sufficient enough to continue on your own.

    As a small reminder I will list everything we finished and everything we were planning but isn’t finished or even started on.

    - Multiple chatrooms: 100% (up to 999 rooms)
    - Private chats: 100%
    - Use tabs for both private chats and chatrooms: 95%
    They work. The design just gets messed up when to many tabs are opened and they no longer fit the clients screen.
    - Private chat settings: 100%
    Able to block all or set to friends only / everyone.
    - Admin functions kick / ban: 100%
    - Color picker for text colors: 100%
    - Smileys: 100%
    - Auto scroll: 100%
    Can be activated / deactivated by client at any time.
    - Profiles: 5%
    The database is prepared. That’s it.
    - Log system: 90%
    It’s there but not in use yet.
    - Admin panel: 1%
    It’s only prepared with some files.

    We never planned to add the ability to use cams for several reasons. But if you think it should be added, feel free to do so.

    If you’re serious about continuing our project please send me an email. I’ll contact you asap.