178

PHP WebSocket Chat Application 2.0

Posted February 15th, 2012 (Updated 30 Jan 2015) 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 Implementation – 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 Implementation – 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 implementation 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.

  • Peter Moskovits

    Nice article – and thanks for the link to the Kaazing blog.

    Peter Moskovits
    Developer Advocate – Kaazing

  • Alex

    I tried the demo. Client says that it’s connected. But server says that count of clients = 0. I used one local machine.

    • Alex

      I just change the port on 8080 and it is working! Thank you. Good product!

  • Alex

    How I insert in this code my php-daemon? It must update the data base and send data to client every millisecond.

    • Flynsarmy

      In class.PHPWebSocket.php below

      $nextPingCheck = time() + 1;

      add

      $nextSecond = time() + 1;

      and below

      if (time() >= $nextPingCheck) {
      	$this->wsCheckIdleClients();
      	$nextPingCheck = time() + 1;
      }

      add

      if (time() >= $nextSecond) {
      	foreach ( $this->wsClients as $id=>$client )
      		if ( $client[2] == self::WS_READY_STATE_OPEN )
      			$this->wsSend($id, "The current time is " . time());
      	$nextSecond = time() + 1;
      }

      The above code will send the time each second. You obviously want milliseconds so you’ll want to play around with microtime().

      • Alex

        Yes, this sample is working. But I can’t use microtime. Cycle “while (isset($this->wsRead[0]))” work one time in second. I don’t understand how to correct it.

        • Flynsarmy

          Change

          $result = socket_select($changed, $write, $except, 1);

          to

          $result = socket_select($changed, $write, $except, 0, 1000);

          In all reality, sending every millisecond is probably too much. Good luck though. I’m sure you can tweak it to something that works.

          • Alex

            Thanks. 10 milliseconds is beautiful for me!

            • Sergey

              Hello. Chat application is good and written in simple way. I tried to modify lines of code above according my project without result. I have problem with managing timing of server. I want that from one client to another client message was arrived within 30 milliseconds. How can I modify server part? Currently it always have delay 1 second. Thank you.

  • David

    Thanks for this. I found it really simple to implement. No more timed polling of the database!

    It’s working fine for everything I need except on Safari on an iPad (iOS 5.0.1) Any thoughts on why that might be?

  • doctorme

    server.php appears to run for me just fine (don’t forget to enable “extension=sockets.so” in your “/etc/php/php.ini” file!).

    But when I go to index.html it says “connecting…” then “disconnected.” immediately.

    I’ve got port 9300 open in the firewall and I’m trying to connect via the LAN ip of the server. What’s wrong?

    • doctorme

      oops, solved.

      I had changed the server IP in “index.html” buf forgot to change the ip address in “server.php” as well.

      server machine: 192.168.1.128

      client machineA: 192.168.1.68
      client machineB: 192.168.1.68
      (same machine, different tabs)

      You’ll need to change index.html LINE 31
      from:
      “Server = new FancyWebSocket(‘ws://127.0.0.1:9300′);”
      to:
      “Server = new FancyWebSocket(‘ws://192.168.1.128:9300′);”

      and you’ll need to change server.php LINE 62
      from:
      “$Server->wsStartServer(‘127.0.0.1′, 9300);”
      to:
      “$Server->wsStartServer(‘192.168.1.128′, 9300);”

      I only put in the ip addresses of the client machines to show what NOT to put in there. In all cases, the correct ip is the ip of the machine that “server.php” is running on.

  • HiepNguyen

    I use Win7. And i don’t know to do this:
    After downloading, simply open a terminal and type
    php5 ./server.php
    Then load it up in your browser.
    Plz help me. Tks

  • rewind

    i tried to run the chat in local and it runs, but.. there’are no way to chat with another client in remote! I used Apache2.2, and the serve is on my computer, if i set up the client with my ip address, the browser answer me that connection in not possible. why? I started the server with localhost ip, because if gave my ip addres it returns me an error

    @Hiep, simply go to start, type ‘cmd’ and in console you must excute this command: php server.php , obviously u need the complete path of the file (for example c:/server.php) then u must simply run the index.html on your browser

    • Flynsarmy

      See here.

  • Nikola

    Thanks man , this is first code who works on mozzila and chrome not just on safari !
    I need this thanks!

  • Joel Martin

    Mistake on your page:

    You say you are using PHPWebsocket (http://code.google.com/p/phpwebsocket/) but in fact you are using php-websocket-server (http://code.google.com/p/php-websocket-server/). Those two projects are quite different.

    PHPWebsocket only supports the Hixie versions of the protocol while php-websocket-server only supports the more recent HyBi/IETF version of the protocol.

    This is important since iOS (since 4.2) still only supports the older Hixie version which means your project cannot be used yet by iPhones/iPads devices.

    Most languages have WebSocket servers available that support both Hixie and HyBi/IETF and automatically detect and use the version that the browser is using. PHP has a notable lack of servers that support both.

    • Jordan314

      Thank you Joel, I saw your stack overflow response and when I used an iOS 6.0 device it worked.

  • ubaidur rahman

    HI I’m using windows os how to run this application in my os please guide me.

    • ubaidur rahman

      I have used php -q server.php in cmd prompt but shows an error like ‘php’ is not recognized as an internal or external command.

      • Flynsarmy

        You’ll have to use c:/xampp/bin/php.exe or whatever the path to your php executable is.

  • Shashi

    Good working example of websockets…
    however i could not get the example working in Internet Explorer8.

    • Flynsarmy

      All version of IE below 10 don’t support web sockets. You’ll have to upgrade to a better (read: any other) browser.

  • Ivan

    Hello,

    I have an issue with getting the code work and I hope you could help:

    Probably something with the encoding, but the index.html says ˆê . However, the console is pretty clear:
    [DATE TIME]: 127.0.0.1 (1) has disconnected

    I have also tried to change both the addresses in server.php and index.html to my local IP address, as well as to change just the port but unfortunately nothing helps – still the same message with the only difference that 192.168.1.101 for example gives 2 disconnect messages right after each other (with the same time) and tries to reconnect after 15 seconds, while 127.0.0.1:9300 – does not.

    I have also checked the php.ini:
    extension=php_sockets.dll
    obviously the sockets are enabled.

    I hope you could suggest something useful : )
    Have you all a nice day.

    • Flynsarmy

      What browsers/versions are you running? You could also try upgrading the PHPWebSocket version if it’s currently newer than the one in my download.

      • Ivan

        Tested with both Firefox 11.0 & Chrome 18
        I will search for later version of PHPWebSocket and write if he issue persists, thank you.

      • Ivan

        A part of the log:

        c:\xampp\htdocs\[LONGER_PATH]\Flynsarmy>php server.php
        2012-04-21 11:50:13: 127.0.0.1 (1) has disconnected.
        2012-04-21 11:55:31: 127.0.0.1 (2) has disconnected.
        2012-04-21 11:55:36: 127.0.0.1 (1) has disconnected.

        The connection from 11:50 was tried from Mozilla, the one from 11:55 – from Chrome.
        The sample above is from 127.0.0.1 on port 9300. (Both on the index.html and server.php)
        Sorry for the additional post, please ‘unite’ them.

        • Flynsarmy

          Do you have a firewall turned on?

  • Steven

    Hi, I would like to replace the IP by the username. In the server.php i try to include the file that i need and retrieve the username in the wsOnMessage function but it’s not working. I’m only able to see the username if i put the code outside of the function and open the file in a browser. I also try with global or session but it’s not working.

    I also looked in the class.phpwebsocket but i’m not quite sure which lines trigger the wsOnMessage function.

    Thanks!

  • Mowgli

    “simply open a terminal and type php5 ./server.php”
    Can you explain this please?

    • Flynsarmy

      Here’s the documentation for opening PHP files from the command line in Windows.

  • Fabian

    I just can´t get it to work… http://labs.addictivity.de/websockets/

    I always get “Address already in use”. Do i have to destroy the old server connection?

    • Flynsarmy

      You can only have one instance of the server open at a time per port. You also probably can’t run on ports that are already in use such as 80 (your web server). See my blog post here for details on seeing what’s currently running on port x.

      • Fabian

        Thank you! Since i´m running this only on webspace and not my server i had to change the port several times ;)

        Used your tutorial to build a multiplayer-drawing-demo: http://labs.addictivity.de/teamdraw. Great stuff!

        • Flynsarmy

          Looks great! Good work

  • Steve O.

    Hello and thanks for this great application.

    I added to wsOnMessage() a function that retrieves messages from a database every second.
    The problem I run into is that the loop locks the process for that Client and no other Clients can connect to it.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    
    // first I authenticated the client via a message exchange, that works fine
     
    while (true) 
    {
        // check if client is not connected, code taken from wsSendClientMessage()
        $Server->log( $clientID."[2] :". utf8_encode( $Server->wsClients[$clientID][2]) );
     
        if ($Server->wsClients[$clientID][2] == $Server::WS_READY_STATE_CLOSING || $Server->wsClients[$clientID][2] == $Server::WS_READY_STATE_CLOSED) 
        {
    	$Server->wsClose($clientID);
    	break; // to exit the WHILE
     
        } else {
     
            // Here's my SQL call from to the DB
            // it sends messages to the Client depending on who he is (previous auth)
     
        }
        sleep(1);
    }

    I never see on the server log “$ip ($clientID) has disconnected.” when the Client leaves, and the server keeps looping until I manually stop the process.

    How do I do a non-blocking loop ?
    How do I properly check if the Client is still there before sending him a message, and if he’s gone remove him from the list of clients connected to the server?

    Thank you for your help!

    • Chad

      You have to run a separate thread for any blocking IO operations that might take a few ms to achieve. One suggestion is to run a deamonized PHP script like worker.php which does this database query, then install memcache, and add the object into memcache as a serialized php object… then from the server.php script, you can read from memcache. This operation takes near 0 ms to retrieve vs a DB query which may take several seconds.

      You can long poll memcache, then the worker.php script after it finishes sleep(1) and then run the query again.

      [worker.php]
      $memcache_obj = new Memcache;
      $memcache_obj->connect(‘memcache_host’, 11211);

      while(true){
      //go to mysql, and get the results as an array (don’t just retrieve the object, you need to put it into an array so it is serializable)
      //assuming your results are in an array $dbResultArray

      $memcache_obj->set(‘db_results’, $dbResultArray);
      sleep(1);
      }

      [server.php]
      $memcache_obj = new Memcache;
      $memcache_obj->connect(‘memcache_host’, 11211);

      //every 1 second or so you can simply
      $data = $memcache_obj->get(‘db_results’);
      //do something with it

      …actually, I’d recommend that for socketio that you don’t use the DB to communicate between clients or to persist data unless it truely needs to be saved indefinately. Most of the time, for a chat app or similar, you might be able to think of a creative solution using memcache or a higher concurrency data storage/messaging bus like zeromq. Mysql will be slow if you are using it for constant realtime message bussing between PHP threads.

    • Arjan

      pcntl_fork is your friend.

      Change

      } else {
       
              // Here's my SQL call from to the DB
              // it sends messages to the Client depending on who he is (previous auth)
       
          }

      to

      } else {
      $pid = pcntl_fork();
      if (!$pid){ 
              // Here's my SQL call from to the DB
              // it sends messages to the Client depending on who he is (previous auth)
      exit;
       }
          }

      A bit late, but maybe it will help someone else too

  • TM

    hi, a great article! first of all.

    I believe this is a fundamental question, but what if you have a rental web server like a web hosting service with PHP5 installed.

    For example, if a person has “abc.com”, how would this person launch this server.php? Does this person only need to place server.php into a php folder? And what would the server address be?ws://abc.com:8080 or ws://abc.com:8080/php ?)

    Thank you for you help

    • Flynsarmy

      You need SSH access in order to fire the server up – you can’t do this with only FTP. The server address would be ws://abc.com:8080

  • Frank

    has anyone got it runing on Safari? That would be great!

  • Anil

    I’m unsure what’s wrong.

    “Connecting…
    Connected.
    You: f
    You: a
    You: sss
    Disconnected.”

    It disconnects me after ~5 seconds. I’m on WIN-7 32bit and using Appserv with PHP5 support.

    -PHP dlls are loaded.
    -Same on port 930 and 8080.
    -Same on 127.0.0.1 or 192.168.2.31 (computer ip)

    What may cause it?

    • Anil

      I just realised I cannot edit my post, but I updated script with some $this->log functions under comments, and this is the result on server script.

      “C:\Users\pc>php -q c:\appserv\www\server.php
      2012-06-05 04:09:22: 192.168.2.41 (1) has connected.
      2012-06-05 04:09:32: // client ready state is open or closing
      2012-06-05 04:09:37: client didn’t respond to the server’s ping request in self:
      :WS_TIMEOUT_PONG seconds
      2012-06-05 04:09:37: 192.168.2.41 (1) has disconnected.
      2012-06-05 04:10:24: 192.168.2.41 (1) has connected.
      2012-06-05 04:10:34: // client ready state is open or closing
      2012-06-05 04:10:39: client didn’t respond to the server’s ping request in self:
      :WS_TIMEOUT_PONG seconds
      2012-06-05 04:10:39: 192.168.2.41 (1) has disconnected.”

      Weird thing is Firewall is off and I have enabled port 930…

      • Flynsarmy

        Perhaps try some high number ports? Try a different browser to confirm it’s not just your browser causing the problems (safari doesn’t work correctly I hear).

        I’m not a windows user so can’t really help you further. You could try going to the PHPWebSocket issues page and see if there’s anything relevant to your issue in there.

        • Anil

          Tried with FF 14 and a freshly downloaded Chrome. I’ve also tried using the port 19376. Still the same. I believe it’s something related to my Windows 7, I’ll have a look at that page.

  • Roy Bellingan

    Tested on Linux , FF & Chromium…
    Perfect, the code I was looking for !

  • ADcomp

    working fine for me [ ubuntu/chromium 18 ].
    Thanks for sharing.

  • Tony

    I have test it, it works under linux localhost, but when i put it to my Debian linux server, Client part display connection… only

    • Flynsarmy

      You have the HTML on your page set to:

      Server = new FancyWebSocket('ws://www.chat.amuzar.com:4321');

      It should be:

      Server = new FancyWebSocket('ws://chat.amuzar.com:4321');
      • Tony

        What about the server.php. it is same as
        $Server->wsStartServer(‘chat.amuzar.com’,4321);?

        • Flynsarmy

          They should both be chat.amuzar.com with 4321.

          You can test if this is working by opening a terminal window and typing:

          telnet chat.amuzar.com 4321

          If your server is running and the firewall is allowing the connection, you should see some text about a visitor leaving the room.

          • Tony

            telnet part:

            [ps105303]$ telnet chat.amuzar.com 4321
            Trying 208.113.225.79…
            Connected to chat.amuzar.com.
            Escape character is ‘^]’.
            -Visitor 1 (208.113.225.79) has left the room.Connection closed by foreign host.

            server part:
            2012-06-19 19:58:32: 208.113.225.79 (1) has disconnected.

            • Flynsarmy

              I’m unsure what your issue is. You’ll have to do some debugging to figure it out

              • Tony

                Ok, I will test it, the onopen does not firing.

  • Quentin

    I am trying to use a secure connexion with wss://mysite.com:12345 as parameter of FancyWebSocket but there a problem during the handshacke. The client can’ t connect to the server.
    Do you know how to fix this?

    • Chad

      Have you tried it in Chrome or a supported browser? Might be that if you are consuming this as a web service using raw TCP you may be missing something like the framing or newlines in the headers.

      • Quentin

        i tryed on firefox and chrome but nothing work with wss. When I look at the header request send by the browser he only send wss://mysite.com:12345. But with a ws who can see the key and other stuff

  • Chad

    If you want to listen on both localhost, 127.0.0.1, an ec2 external facing IP, or some other unknown IP, you can use 0.0.0.0. This is identical to a wildcard on linux based machines (http://php.net/manual/en/function.stream-socket-server.php).

    Was much easier and more portable when changed to 0.0.0.0 between machines :) This websocket server is primo!

  • Ildar

    How can i send message to a server side from another server side?

  • kp

    How can this script be used to incorporate desktop notifications rather than a chat message where the messages are coming from a database?

  • nugelomaniac

    how to input data from terminal / telnet and send to web browser / #log textarea

    Thanks

  • Yashvir Singh Prince

    How can we validate client browser so that only valid client may connect to the server because any one can connect to the server just putting ip address of server in place of 127.0.0.1.

    • Yashvir Singh Prince

      I have found a solution we can validate client based on message passing.

      • Flynsarmy

        You should share what you’ve found. Perhaps others might find it useful.

        • Yashvir Singh Prince

          We can validate client after connection using sending a message with secret key but main problem is that how can we know that client is valid or not before connection and after validation it may only connect to chat server.

  • Yashvir Singh Prince

    How can we perform private and group chat in this chat server.

  • Arjan

    Hi,

    First:

    -Can you make this script so that it wil fork for every connected user, and sending messages go through the forked php?
    There is an example of that here: http://www.phpclasses.org/browse/file/37977.html

    Because: if an user has (really really) low bandwidth, the whole scripts will wait for that message to be send to that users, and theoreticly can “hang” the server i think ;)

    Second:
    I am stress testing this chat with some “bots” ( http://93.180.66.90/index.php ), which do some random actions, like reloading the chat / loggin in again / sending text / changing room)

    Positive side: It works good, tested up to 50 users, sending messages every second or so and dooing random actions when thet are just connected and dooing stuf.

    But i am having some issues, summed up:
    -when the user does not have enough bandwidth (like me opening 50 clients from same connection and every client reloading chat every 5 seconds) and when i stop and start server (and also without stop and start) i get all kinds of php warnings (like undefined offsets, and user 1 disconnecting multiple times).

    This makes me wonder – can a user, do stuff to the chat to make it go “down” if he fakes low bandwidth with some program.

    – i did alter the sending messages to fork, so i dont know if you can reproduce the warnings/errors) -> allo i did was to fork the sending messages like: $pid=pcntl_fork(); if (!$pid){ //send sessages; exit;}

    On the positive side i made:
    -Added fallback sockets with flash if browser does not support Websockets
    -Added webcam with red5 and javascript.
    -Priverooms
    -private messages
    -disable user watching webcam
    -block private messages
    -switch rooms / create password rooms / create camonly rooms
    -some more thing

    Still working on it, example http://93.180.66.90/testindex.html

    Oh and feel free to ask specific questions, i do know this message probably will seem to be from a nutty dude which just woke up and is goont to get some more cofee :D

    Best regards,
    Arjan

  • Kirito

    How can i display a list of connected users and allow the name change? Thx :)

    • arjan

      in server.php create an array for the information:

      $userInfo = array();

      inside function wsOnMessage:

      global $userInfo;
      if (substr($message,0,5) == "/list"){
      	$message = "";
      	foreach($chatInfo as $info)$message .= $info['clientID']." ";
      }
       
      if (substr($message,0,7) == "/nick "){
      	$userInfo[$clientID]['nick'] = substr( $message, 7, strlen($message) -7);
      	$message = "user $clientID changed nick to ".substr( $message, 7, strlen($message) -7);
      }
      }

      i didn’t test this code, but should work.

      • arjan

        Whoops $info[‘clientID’] should be $info[‘nick’]

  • abhishek

    Hello,
    thanx for the article and code. Its running fine on my local server.
    But can any one please tell me how to run this on my server???

    • Arjan

      if you have shell access:

      php -q server.php > /dev/null &

  • willbe

    Hi, first, thanks for the sample. It works right out of the box. But I have a prob: I have the server running and the client disconnected over night. Where should I modify to keep client alive as long as it connected?
    thank you.

    • arjan

      The client is already configured for that.

      Reasons for a disconnect could be, that your isp or server connection was offline for a little while. just a few seconds is enough to disconnect a client.

      You could change the number in the class file for “client_ping” or whatever it was called, i beleive it was set to 5, you cound make it higher, but i have no clue if that will help. Certainly not if the connection is gone for more than the seconds specified there.

      A stable connection in for both parties is needed for such an application ie. server and client.

      [Note to Flyn and his army]: Todo. Adjust websocket script to fork client connections :p

      • willbe

        Thank you for your reply. I will check if my server went off line.

  • Syl

    Hello,

    Thank’s it works well, anyway, someone could help me of that http://stackoverflow.com/questions/13541762/php-websocket-chat-private-conversation ?

    Best regards.

    • Arjan

      If you’re lazy, you can let the script run multiple times om multiple ports.

      If you’re not you can modify the script, its really not so hard. create a global array $userInfo then on connect $userInfo[ $userID ]=array( $roomID ); when a user connects;

      Pass roomID thrugh php

      When sending messages, only send to user in the same room
      foreach ($userInfo as $key => $value)
      if ( $value[0] == $userInfo[$userID]) wsSend( $key, $message);

  • willbe

    Hello another question I would like to ask>
    I have a php script that listens to another app (named A) on different port. Currently that php script will send response to web client when there is an event occur on the app A. I would like to integrate that script in to this, and I think the wsOnMessage function will be called from that script. My question is can I create a function in wsOnMessage that listen to the app A and broadcast to web clients if anything happen. Thank you

    • willbe

      I think I repeated the question that has been answered by Flynsarmy on February 24, 2012 at 12:14 AM. Thanks again.

  • muzztein

    Can you give an example of a BOT sending text to the conected users?

    • Arjan

      Hi,

      I would like to see a bot functionality too, with diffrent user names to stress test.

      For now, we can insert into the html

      function writeaLine(){
      send( Math.random() );
      window.setTimeout( 'writeaLine()', 1000);
      }
       
      window.setTimeout( 'writeaLine()', 3000);

      And then open 100 tabs with the url :D
      I managed to open around 60 before my pc almost broke down ;)

      • muzztein

        it is not posible?
        how can I do it for my self?
        any direcctions?

  • jorge

    It just works!!
    And above all event oriented, in a language like php, fantastic!

  • Troy McCormick

    I know this post is ancient, but wanted to ask here anyway. Is there any way to change the server.php config after it’s been started from the command line? For example, updating the WS_MAX_CLIENTS to be 10 instead of 100 after it’s already been started without having to stop it, make your change, then restart it? That would be cool… :)

    • Flynsarmy

      You could change the variables in the PHPWebSocket class from const to standard public variables then on a timer load from a config file (or database). then just call $Server->WS_MAX_CLIENTS = <num>

      You could even just set up a command that could be accepted through the chat which would be the easiest solution.

      • Troy McCormick

        Ok…thanks for that. Points me in the right direction. Now what if I want to run a game from this websocket. How would I implement timing? You wouldn’t want that on the client side as it could then be manipulated.

        For example: Say it is Player A’s turn. He has 25 seconds to make his play before a play is made for him by the server. How would you accomplish something like that?

        Thanks for the great code by the way! :)

        • Arjan

          Some mumbo jumbo:

          $noPlayers = 2;
           
          function setTurn($clientID){
          global $moveTime, $currentTime, $playerID, $noPlayers; 
          if (!isset($playerID)  || $playerID &gt; $noPlayers) $playerID =1;
           
          $moveTime = 25;
          $currentTime = time();
          $playerID = $clientID;
           
          //send message to user which players turn it is here
           
          }
           
          function checkTurn(){
          global $moveTime, $currentTime, $playerID, $noPlayers;
           
          if ( $currentTime + $moveTime &lt; $time()){
          // Make your move here
          setTurn($playerID+1);
           
          }
           
          }
           
           
          // if player made  a move, call setTurn();
           
          //every second or so poll from clientside send(&quot;/check&quot;);
           
           
          if ($message=&quot;/check&quot;){
          checkTurn();
          }
          • Troy McCormick

            Wouldn’t this result in a race condition? If you have 9 players waiting on 1 players action, wouldn’t all 9 of the other players hit the checkTurn() function at the same time, then subsequently update the turn to playerid+1?

            • Arjan

              I dont think so, i beleive everything is executed in a serial way. Flyn correct me if i am wrong, thanks.

              But if you want to make sure you could change

              function checkTurn(){

              to

              function checkTurn($clientID){

              and

              if ( $currentTime + $moveTime < $time()){

              to

              if ( $currentTime + $moveTime < $time()
              && $clientID == $playerID ){

              and

              checkTurn();

              to

              checkTurn($clientID);

              And add a check on the wsOnClose function if the player exiting is one who’s move it is currently

              global $playerID
              if ($clientID == $playerID){
              	$playerID++;
              	if ($playerID > $noplayers)$playerID=1;
               
              	// do a foreach here to see who is the next player that is still connected
               
              	setTurn($playerID)
              }

              or something like that, but i think its easier to ask flyn if it is indeed executed serially that way you dont have to change anything

  • amesh

    Hi,

    it’s very help full to me but i want to modify the script one to one chat please help me

  • urgotto

    THX !!!
    avesome code, first example working on my web server.

  • JoeP

    Hey, I’m really enjoying this library! I’ve even edited it so that it provides one-to-one chat based on a username/password table in SQL. I have it all set up and working in WAMP over my local network.
    I am, however, having trouble making it accessible over the internet. To make it work on my local network, I changed this lines in the index and server files to use my local IP:

    Server = new FancyWebSocket('ws://l27.0.0.1:9300');
     
    $Server->wsStartServer('ws://l27.0.0.1',9300);

    I’m now trying to make it accessible to people on the internet from my home computer. Here are the actions I’ve taken:

    • Change the above-mentioned lines of code to use my outward-facing IP
    • Setup port forwarding on port 80 to my machine
    • Put WAMP online

    After doing this, I try starting server.php from the command line, but I get the following error:

    “socket_bind(): Host lookup failed [0]: The requested name is valid, but no data of the requested type was found.”

    Any help you can offer would be greatly appreciated!!

    • Arjan

      “Setup port forwarding on port 80 to my machine”

      Hi Joep, dont forward it to port 80, just use the same portnumer on your machine.

      Just forward port 9300 to port 9300 on your local machine and dont forget to let your firewall pass request to your local machine, i beleive you need to open UDP, but you can open TCP and UDP if you want to be sure.

      socket_bind(): Host lookup failed [0]: The requested name is valid, but no data of the requested type was found.”

      Do you have a firewall? Windows comes standard with a outbound-firewall you need to allow connections to this port on UDP, dont know if you use linux of windows.

      • JoeP

        Arjan, thanks a lot for the information. I will give that a try this evening when I get home. I’m using Windows. As for firewalls, I have only what Windows comes with.

  • JoeP

    It looks like a lot of people are curious about one-on-one chat rather than a chat room. Here is a possible solution that does not require passwords. Here’s a little code to get you started:

    1. To make room for usernames in wsClients, go to the “wsAddClient” function in class.PHPWebSocket.php. Edit line 253 (the one under “// store initial client data”) by adding an extra comma and empty string before the closing parenthetical so that the line now looks like this:
      $this->wsClients[$clientID] = array($socket, '', self::WS_READY_STATE_CONNECTING, time(), false, 0, $clientIP, false, 0, '', 0, 0, '');
    2. Next, you need to enable server.php to accept and interact with JSON objects so that it knows when the client is sending a username rather than a message. To do this, go to the “wsOnMessage” function in server.php. You should delete or comment everything within this function after line 17.Now paste the following code in:
      //Decode the JSON
      $message = json_decode($message);
       
      //Variables the JSON will fill
      $requestType;
      $username;
      $password;
      $to;
      $from;
      $msg;
       
      //Unpacking the JSON into the above variables
      foreach($message as $k => $v){
      	if($k == "requestType")
      		$requestType = $v;
      	if($k == "message")
      		$msg = $v;
      	elseif($k == "to")
      		$to = $v;
      	elseif($k == "Uname")
      		$username = $v;
      	elseif($k == "Pword")
      		$password = $v;
      }
       
      //Actions if the client has sent a message with requestType 'message'
      if($requestType == 'message'){
      	$recipientID;
      	$int = 1;
      	foreach($Server->wsClients as $user){
      		if($user[12] == $to){
      			$recipientID = $int;
                              BREAK;
                      }
      		$int++;
      	}
       
      	$Server->wsSend($clientID,"Me: ".$msg);
      	$Server->wsSend($recipientID,$sender.": ".$msg);
      }
       
      //Actions if the client has sent a message with requestType 'login'
      elseif($requestType == 'login'){
              /*MAKE SURE THIS USERNAME HAS NOT ALREADY BEEN USED*/
      	$Server->wsClients[$clientID][12] = $username;
      	$accessGranted = 1;
      }
    3. Now edit your front end to send JSON objects. Be sure to stringify them first. With this code, you should be able to make it work. Keep in mind, there are some big holes with this implimentation, but it should get you started towards making your chat client one-on-one.
    4. Good luck!!

    • Arjan

      Hi Joep, you have some nice looking code there :D

      The way i solved it is, add a space before each “message”, so you can use the 1st character as a command.

      Maybe not so elegant, but it requires less bandwidth, and is easier to implement i guess.

      • JoeP

        That’s a really good idea, Arjan! I just hate dealing with substrings, so considering that there are multiple parameters to send for a message (message type, recipient, and message) and a login request (message type, username, and password), this seamed reasonable.

        But you are certainly right about your way being simpler and even somewhat more efficient!

        If you have any suggestions for the question I posted before the solution you commented on, I’d be really grateful! I’ve been trying to get it working online for several hours.

        • JoeP

          Nevermind, you could just use explode() or something. Your way is super simple, Arjan!

          • Arjan

            Thats true;
            A normal message from clientside:

            send(" hello")

            Setting a username:

            send("Umyusername"); // or U|myusername

            private message could be like:

            send("P|1|message"); // to client id 1

            check out what i made of it. Included webcam, punlic rooms, password protected rooms, private messages, invite users to room and what else not: http://123chatbox.nl/chat/chat.php?chatnaam=Demo&geslacht=M

            Only your code probably looks nicer ;)

            Best regards
            Aryan

            • JoeP

              Wow, your implementation is impressive! I like all the options – webcams seems especially cool. And is Dutch your native language? (I assume so from the link.) If so, your English is spectacular! I wouldn’t have suspected English to be a second language for you.

        • JoeP

          So I did what you suggested, but I’m still unable to get it working. The closest I’ve been able to come is when I switch my external port forwarding port to port 80 and my internal one to port 9300.

          When I do this, the browser requests the page, but after a few seconds of trying to load, the command line from which I run server.php says that client 1 disconnected (it nenver says client 1 connected) and then the browser says the file was sent with an incorrect MIME type and downloads something.

          This confuses me since I get no MIME type errors while running it over my local network. Sorry to keep asking questions, but do you have any suggestions?

      • brandon

        do you have a sample of this code. I am having heck trying to implament

  • Arjan

    Question; Are there users here with high volume traffic who want to test either the text-chat or the webcamchat script i made out of this script?, of course free of charge

    Just drop a note, i can setup within 24 to 48 hours.
    You will be hosting the client side script yourself, which i will send to you. Server side hosting will be on my account.

    Limitations would be bandwidth usage up to 200GB, because of the current hosting package it is set on, which would be no problem with the textchat, but webcamchat could use that in a 2-3 days with 100 users online.

    I am willing to offer it for the period of 3 months, or until the 200GB bandwidth is reached.

    Requirments; at least 1000 visitors a day for proper testing, preferably more

    Demo:
    http://123chatbox.nl/chat/chat.php?chatnaam=Demo_User&geslacht=M

    Please let me know at info@123chatbox.nl or leave a message here with your email :O.

    • JoeP

      Arjan, I have a question about your site:

      How did you get server.php running once it was on your webhost’s server?

      • Arjan

        On linux:

        php -q server.php > /dev/null &

        I use a VPS. Maybe it’s possible on shared hosting if you start it through Cpanel -> Crontab, but i guess that will have a TimeOut that is hardcoded in PHP.ini, you could try set_time_limit(0);

    • mongy910

      Hey Arjan your chatroom looks amazing! I own the site and we get around 3000 visitors a day. If you are still offering a test of your script, I would really be interested. Thanks!

  • Andres

    Arjan,

    Did you figure out how to fork new child processes for each client that connects?

    • Arjan

      Yes, i figured that out ( see the example script i put a link to many post above)
      But no, i have not implemented it, i was somewhere hoping flyn would do that :D

      I just use pcntl_fork to fork the sending, this adds some overhead, but i ran some performance test, and can’t really remember but it could handle plenty of messages. And of course you can send 1 message to all users in 1 forked process.

      Test the performance yourself. (ps. i managed to make a server inresponsive, so dont test on a production server/vps :))

      <?php
      $start = time(); // < micro time, but you know lazy and all
      $insane = 10000;
      for ( $x=0;$x<$insane;$x++){
      	$pid = pcntl_fork();
      	if ( !$pid){
      		// yay we sucsesfully forked the b*tch
      		exit;
      	}
      }
      echo "Forked $insane times in ".time()-$start;

      Please let me know your performance, thanks.

    • Arjan

      Hi,

      Do you have any specific reasons for wanting to fork for each user?

      Somewhere it makes sense to me. This way the user will always get all messages in the order that they were sent from the server, and you will not see anomalities in a chat.

      When using 1 fork to send all messages each time, depending on the conditions, some messages may arrive in the wrong order, but this will only be visible when the server gets a higher load and/or when a client takes a fraction of a time more to accept a message.

      For. ex. what would happen if the command for swiching to room “A”, came before the switching to room “B” – the chat would reflect a wrong room count for the user who had this delay.

      Since i did my stresstes from 1 connection, this is also why i didn’t detect this while testing. Since 1 connection does not meet te requirments to delay the sending of messages to users in the queu.

      So i think there is no doubt – is you want to make a good chat-script, you will have to build in a persistent fork for user so messages arrive in the correct order.

      Flyn, any chance you are willing to spent some time on this? Else this will cost me at least a few hours. thanks for the response.

      Aryan

      • Andres

        Well I was only concerned about forking new processes for each user in case of users with low bandwidth which can cause the daemon to hang while all the messages are being sent.

        I figured I would make a ‘queue’ system of messages that are to be sent to each individual client, but then messages can be sent out of order, like you mentioned.

        For now, I am creating a forked process every time I am sending a batch of messages to clients.

        • Arjan

          I did some additional testing; messages longer in length will arrive later than messages shorter in length, send right after each other.

          For Ex.:

          “Hi” will arive sooner, this can cause problems depending on the functions buid in the chat

          I must make a solution for this. Please leave a message if you have some working code.

  • Arjan

    Flyn,

    What would be the ideal way of forking childs?

    A) master receives all websocket data -> sends data to childs, who then send it to user

    B) Master is just the communication manager for childs, each child listens to websocket data and sends data to childs -> who then send it to the user?

    Does it make any diffrence?

    Best regards,
    Aryan

    • Flynsarmy

      Communicating between processes is probably slow. I’d create ‘nodes’ or groups of people chatting. When a node fills (each node can have a predefined maximum number of clients) you start a new node. Every time a person speaks you communicate that to the other nodes.

      That’s possibly how I’d do it but this is by no means my area of expertise so take my idea with a grain of salt.

      • Arjan

        Thanks for the quick reply.

        Do you mean you would fork messages for each node? or just send it without forking.

        Do you think users with low bandwidth could slow down the sending process ?

        The problem which i am trying to solve, is that when a fork is used to send messages in batch, short messages arrive earlier than long messages.

        Ps. i did a test, i copied example 2 from http://php.net/manual/en/function.socket-create-pair.php

        Put in a for loop in both the parent and child, of 10000000 iterations, and it finished in 33 seconds .

        So that would be around 606060 messages per seconds between unix sockets. i think that would be usable.

        Best Regards,
        Aryan

        • Flynsarmy

          Messages occasionally appearing out of order probably doesn’t matter too much in an online chat room – it already happens in protocols like MSN.

          My other issue with using one socket per client is that it’d probably take more resources on the server than one socket for a group of clients.

          I’m not really able to help here as it’s out of my league. Your best bet would be to whip up a version of the script for each method you’ve come up with, stress test, analyse and compare the results.

          Might also be worth looking into how protocols like XMPP work.

          • Arjan

            I just finished some testing in firefox with a bandwidth throttler on 8k and 28k speeds, and there seems to be no problem at all.

            -When a client is receiving a large message, while receiving they can still send and receive messages

            -When a client is sending a large message, while sending they still can receive messages, but not send another one until the previous message is sent

            Nowhere in the process does it delay the php-socket-server. (as far as i could see)

            So the only reason you should/could use pcntl_fork is if you are running a heavy multi_core (>2 cores) and have a lot of connected clients (100+).

            On the account of messages arriving earlier:
            you could number each message, and on the client-side javascript compare if the messages are sequental, if not you could delay the message for 0.1 to 0.5 seconds.

            Assuming commands for switching rooms e.a. are pretty short – that should not be a problem also.

            Thank and have a nice day.
            Aryan

            • Bjorn

              I really like what you’ve done with this script so far. Do you mind releasing (part of) your source? I’m from Holland like you and I plan to make a large chatsite. Just not interested in webcam support because of the Dutch law saying you have to save everything for at least 2 years.

              I’m not planning to just use your code and go for it ofcourse. There’re still lots and lots things that have to be changed / added for my idea to work out.

              • Arjan

                What part would you like help with? I can guide you in the right direction.

                Releasing the source code is 1 step too far, i worked on it for almost 2 months now, unless you are willing pay for the hairtransplant needed for recovering the hairs i lost during this period :P.

                The dutch law only requires the “headers” of a request to be saved, which would be identical to your logfiles. You are not required to keep logs of the content of those requests – which would be impossible the size would be too much.

                By the way;
                -What do you think of the trivia game i build in?
                -Next up is poker, chatroulette etc etc.

                Best regards,
                Aryan

                • Bjorn

                  Well I didn’t really get the chance to test anything because your chat disconnects me before I can send anything.

                  If you could just help me get started by giving me what I need to set up usernames, I could probebly figure out anything else I need myself. Pretty good at figuring out stuff once I get the hang of something :P

                  What worries me a bit is the last posts about forking and using nodes. Although I do understand nodes a little bit, that talk is way over my head. If there’s anything I understood from it, it has something to do with a client begin unable to send a message if it’s still in the process of sending / receiving one. If all goes as planned, I should expect about 3 to 500 unique users online at daytimes so I guess this is something I should worrie about.

                  About the law I’m not really sure. I know for a fact that any text-messages must be saved for at least 2 years. Thought it was the same for images / cam-streams.

                  • Arjan

                    >Well I didn’t really get the chance to test anything because your chat disconnects me before I can send anything.

                    Yes yes, that’s me buildin in more options. Just finished building in sending images over chat.
                    Now busy with logfiles.

                    Just curious, which browser did you use.

                    >If you could just help me get started by giving me what I need to set up usernames

                    Make a form, with action set to chat page.

                    On the chatpage you write out javascript thrugh php:

                    echo "[script]var username='{$_GET['chatnaam']}';[/script]"

                    on the onload of websocket you can do:

                    send("/u|"+username);

                    On the serverside you can do

                    $tmp = split("|", $message);
                    if ($tmp[0]=="/u")
                       $Server->wsClients[13] = $tmp[1]; // or create your own array to save the info

                    Then you must let the users know the chatname used
                    for ex:

                    $Server>wsSend($allUsers,"/u|".$clientID."|".$Server->wsClients[13]);

                    Then on the javascript side you make a function

                    tmp = message.split("|");
                    var usernames;
                    if ( tmp[0] !== undefined && $tmp[1] == "/u")
                    usernames[tmp[1]]=tmp[2];

                    then you process it in your javascript chat.

                    Regards,
                    Aryan

                    • Bjorn

                      Thanks that should get me up te speed. I used Mozilla Firefox.

  • muzztein

    How can I make a php script that sends a messege to the users connected to a running chat?

  • starbeamrainbowlabs

    Does anybody know of a secure websockets implementation similar to this one or how I can modify this websockets server so that it is secure?

    I have just built an app on top of it and realised that it is not a secure websockets server (this is important so that thos ebehind proxies can connect).

    • Aryan

      It seems to me a problem of the proxy server not having your chosen port open, dont see how the wss protocol will fix that

  • Max

    how could you do this without shell access?

    • albert

      I wanted to ask the same thing

  • Shanil

    i cant connect to it i get like
    “Connecting…”
    and after like 20 seconds i get
    “Disconnected”
    please help me with this
    i’m hosting it on a web server

  • Sajeeb

    when i run it on my web server it shows connecting and then after like 20 seconds it displays disconnected. could anyone please help me with this.

  • 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.

    • Moinul Alam

      change the IP in server.php to your external IP, which can be found by searching in google “what is my IP”

  • 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.

  • Vincent Chu

    I’m on the same boat as Ayps …

    Its working great behind my firewall but unable to get it working outside.

    I even have several subnets which they all work behind the firewall. I have both port 80 and 8080 (changed from 9300) allowed and forwarded on the firewall.

    webpage loads, but after connecting… i get disconnected.

    in servers.php i used ‘0.0.0.0’, 8080 and in the index.html i used the IP which is 192.168.1.58

    anything else i’m missing?

  • Mir

    Hiya

    Thanks for the tutorial, but when i try your code at my local (xampp 1.8.1) i can’t make it work. In browser i saw message “connecting”, then “connected” but i can’t send and receive message after that ( i tried to communicate between chrome and firefox ), after a while i got message “Visitor 1 (127.0.0.1) has left the room.” and then disconnected. Can you please help troubleshot this problem ? I have tried with several port and make sure all of them are free.

  • Ryan

    How do I access session data?

  • Arjan

    Hi Flynsarmy,

    Are the messages that are sent from the server to a client asynchronous or synchronous?

    In the chat application i made, it seems like it is asyncronous.

    When a user connects to the server i send a message to the client to create an array: userList[clientID];

    But sometimes, when it tries to access the userList[number] array, i get a javascript error: cannot read property undefined of [number]

    it seems like the actions i send for an userID are received, before javascript gets the messag where it is told to “create” an user in the userList array…

    Im really confused, excuse my bad english.

    Regards Arjan

  • abdul

    Hi there..
    I downloaded you script. and place it into htdocs folder in xampp which i have installed on D-Drive.. and tried the command php5 ./server.php but dos says php5 is not recognized as internal or external command. please help

  • Arjan

    Dear Flyn,

    I have worked for months on a decent chat script. However, i have moved my site from http to https.

    Now Chrome throws an error: “Cannot make insecure websocket over https, please use secure websocket”.

    When i change ws:// to wss:// in the PHPWebSocket class, the script fails to work.

    Could you please help, and tell me, if this script should work with secure websocket connections too? or if not, what should be changed?

    Thanks a lot in advance, i hope you can help me with this.

    Regards, Aryan

    • flynsarmy

      Hey Arjan, good to see you’re still around :)

      The websocket package I built my script on top of hasn’t been updated in over 4 years and doesn’t support secure websockets. Your best bet would be to find a much better package such as Ratchet and use that instead.

      • Arjan

        Hi Flyn, that sure isn’t what i was hoping to read :S

        That means i have to learn to use (required by ratchet):
        symfony/http-foundation: ~2.2
        symfony/routing: ~2.2
        react/socket: 0.3.*|0.4.*
        guzzle/http: ~3.6

        And on top rewrite the current adjustments/scripting i made, which is gooing to be a lot of work.

        Is there any chance you could look into “stunnel”.

        It looks to be a “tunnel” program, which accepts secure connections from port a , and forwards it to unsecure port b.

        I have been trying for the last few hours, and i manage to set it up so port 9301 forwards to port 9300 where your socket script is running. But…as soon as the socket connects, it immidiately disconnects :S

        Anyway, if you are willing to check this out, here is my config for stunnel, it will save you some time configuring.
        key = /home/site/ssl.key
        cert = /home/site/ssl.cert
        CApath = /home/site/ssl.ca
        socket = l:TCP_NODELAY=1
        socket = r:TCP_NODELAY=1
        ;setuid = nobody
        ;setgid = nobody
        pid = /var/run/stunnel_websocket.pid
        debug = 4
        output = /var/log/stunnel_websocket.log

        ;i suppose you have to either use https and uncomment websocket below, or the other way around

        ;[https]
        ;accept = http://www.flirtzo.eu:9301
        ;connect = http://www.flirtzo.eu:9300

        [websocket]
        accept = http://www.flirtzo.eu:9301
        connect = http://www.flirtzo.eu:9300

        Regards Arjan

      • Arjan

        It’s solved, i managed to get stunnel working to connect secure on port 9301 with wss:// and forward it unsecure to port 9300 where your websocket script resides. Just make to use hostname and not ip in the javascript, and it will work.

        • http://microkeeper.com.au/ Joel Davis

          Hi Arjan,
          I’m in exactly the same boat as you were.
          Everything is working really well with ws:// as soon as I change it to wss:// the script fails.
          stunnel looks too complicated for me, would you be interested in helping me implement stunnel?
          Happy to pay you for your time.

  • donat

    I posted my chat, it works fine on local but it has problem like this: Warning: socket_bind (): unable to bind address [98]: Address already in use in: … if I put online what does it mean this error message

    • http://microkeeper.com.au/ Joel Davis

      Error 98 means this port is in use, I changed it from port 9300 to port 41000 and it worked.

  • Hien

    how to “style” for messages on server.php and don’t edit this file ? (Mean, I want get messages from server.php to index.html, ex: “Visitor 2 (127.0.0.1) has joined the room.”)

    Plz help !
    Thank you,

  • Bhautik

    I have implemented this script on my local machine, I changed port 9300 to 80 and set url path in index.html file, but i am getting disconnected message in application. any suggestion?

    • http://microkeeper.com.au/ Joel Davis

      The url path should not be changed to a file, the url should always be the IP address or domain name where the web socket script is running. The port should not be 80 as this is reserved for HTTP traffic and will not work. The port should be set to 9300 or any unused port, where you have your server.php script running.

  • manoj

    this is a very good article about one to many conversation . what about one to one conversation how can we implement this ??

  • gerardo pacheco

    how I can send a message
    yet user or connection
    only that user has