How to create websockets server in PHP

242,704

Solution 1

I was in the same boat as you recently, and here is what I did:

  1. I used the phpwebsockets code as a reference for how to structure the server-side code. (You seem to already be doing this, and as you noted, the code doesn't actually work for a variety of reasons.)

  2. I used PHP.net to read the details about every socket function used in the phpwebsockets code. By doing this, I was finally able to understand how the whole system works conceptually. This was a pretty big hurdle.

  3. I read the actual WebSocket draft. I had to read this thing a bunch of times before it finally started to sink in. You will likely have to go back to this document again and again throughout the process, as it is the one definitive resource with correct, up-to-date information about the WebSocket API.

  4. I coded the proper handshake procedure based on the instructions in the draft in #3. This wasn't too bad.

  5. I kept getting a bunch of garbled text sent from the clients to the server after the handshake and I couldn't figure out why until I realized that the data is encoded and must be unmasked. The following link helped me a lot here: (original link broken) Archived copy.

    Please note that the code available at this link has a number of problems and won't work properly without further modification.

  6. I then came across the following SO thread, which clearly explains how to properly encode and decode messages being sent back and forth: How can I send and receive WebSocket messages on the server side?

    This link was really helpful. I recommend consulting it while looking at the WebSocket draft. It'll help make more sense out of what the draft is saying.

  7. I was almost done at this point, but had some issues with a WebRTC app I was making using WebSocket, so I ended up asking my own question on SO, which I eventually solved: What is this data at the end of WebRTC candidate info?

  8. At this point, I pretty much had it all working. I just had to add some additional logic for handling the closing of connections, and I was done.

That process took me about two weeks total. The good news is that I understand WebSocket really well now and I was able to make my own client and server scripts from scratch that work great. Hopefully the culmination of all that information will give you enough guidance and information to code your own WebSocket PHP script.

Good luck!


Edit: This edit is a couple of years after my original answer, and while I do still have a working solution, it's not really ready for sharing. Luckily, someone else on GitHub has almost identical code to mine (but much cleaner), so I recommend using the following code for a working PHP WebSocket solution:
https://github.com/ghedipunk/PHP-Websockets/blob/master/websockets.php


Edit #2: While I still enjoy using PHP for a lot of server-side related things, I have to admit that I've really warmed up to Node.js a lot recently, and the main reason is because it's better designed from the ground up to handle WebSocket than PHP (or any other server-side language). As such, I've found recently that it's a lot easier to set up both Apache/PHP and Node.js on your server and use Node.js for running the WebSocket server and Apache/PHP for everything else. And in the case where you're on a shared hosting environment in which you can't install/use Node.js for WebSocket, you can use a free service like Heroku to set up a Node.js WebSocket server and make cross-domain requests to it from your server. Just make sure if you do that to set your WebSocket server up to be able to handle cross-origin requests.

Solution 2

As far as I'm aware Ratchet is the best PHP WebSocket solution available at the moment. And since it's open source you can see how the author has built this WebSocket solution using PHP.

Solution 3

I've searched the minimal solution possible to do PHP + WebSockets during hours, until I found this article:

Super simple PHP WebSocket example

It doesn't require any third-party library.

Here is how to do it: create a index.html containing this:

<html>
<body>
    <div id="root"></div>
    <script>
        var host = 'ws://<<<IP_OF_YOUR_SERVER>>>:12345/websockets.php';
        var socket = new WebSocket(host);
        socket.onmessage = function(e) {
            document.getElementById('root').innerHTML = e.data;
        };
    </script>
</body>
</html>

and open it in the browser, just after you have launched php websockets.php in the command-line (yes, it will be an event loop, constantly running PHP script), with this websockets.php file.

Solution 4

Need to convert the the key from hex to dec before base64_encoding and then send it for handshake.

$hashedKey = sha1($key. "258EAFA5-E914-47DA-95CA-C5AB0DC85B11",true);

$rawToken = "";
    for ($i = 0; $i < 20; $i++) {
      $rawToken .= chr(hexdec(substr($hashedKey,$i*2, 2)));
    }
$handshakeToken = base64_encode($rawToken) . "\r\n";

$handshakeResponse = "HTTP/1.1 101 Switching Protocols\r\nUpgrade: websocket\r\nConnection: Upgrade\r\nSec-WebSocket-Accept: $handshakeToken\r\n";

Solution 5

I was in your shoes for a while and finally ended up using node.js, because it can do hybrid solutions like having web and socket server in one. So php backend can submit requests thru http to node web server and then broadcast it with websocket. Very efficiant way to go.

Share:
242,704

Related videos on Youtube

Dharman
Author by

Dharman

Stack Overflow elected moderator since 28th March 2022. Warning: You are wide open to SQL Injections and should use parameterized prepared statements instead of manually building your queries. They are provided by PDO or by MySQLi. Never trust any kind of input! Even when your queries are executed only by trusted users, you are still in risk of corrupting your data. Escaping is not enough!

Updated on March 29, 2022

Comments

  • Dharman
    Dharman about 2 years

    I am looking for a simple code to create a WebSocket server. I found phpwebsockets but it is outdated now and doesn't support the newest protocol. I tried updating it myself but it doesn't seem to work.

    #!/php -q
    <?php  /*  >php -q server.php  */
    
    error_reporting(E_ALL);
    set_time_limit(0);
    ob_implicit_flush();
    
    $master  = WebSocket("localhost",12345);
    $sockets = array($master);
    $users   = array();
    $debug   = false;
    
    while(true){
      $changed = $sockets;
      socket_select($changed,$write=NULL,$except=NULL,NULL);
      foreach($changed as $socket){
        if($socket==$master){
          $client=socket_accept($master);
          if($client<0){ console("socket_accept() failed"); continue; }
          else{ connect($client); }
        }
        else{
          $bytes = @socket_recv($socket,$buffer,2048,0);
          if($bytes==0){ disconnect($socket); }
          else{
            $user = getuserbysocket($socket);
            if(!$user->handshake){ dohandshake($user,$buffer); }
            else{ process($user,$buffer); }
          }
        }
      }
    }
    
    //---------------------------------------------------------------
    function process($user,$msg){
      $action = unwrap($msg);
      say("< ".$action);
      switch($action){
        case "hello" : send($user->socket,"hello human");                       break;
        case "hi"    : send($user->socket,"zup human");                         break;
        case "name"  : send($user->socket,"my name is Multivac, silly I know"); break;
        case "age"   : send($user->socket,"I am older than time itself");       break;
        case "date"  : send($user->socket,"today is ".date("Y.m.d"));           break;
        case "time"  : send($user->socket,"server time is ".date("H:i:s"));     break;
        case "thanks": send($user->socket,"you're welcome");                    break;
        case "bye"   : send($user->socket,"bye");                               break;
        default      : send($user->socket,$action." not understood");           break;
      }
    }
    
    function send($client,$msg){
      say("> ".$msg);
      $msg = wrap($msg);
      socket_write($client,$msg,strlen($msg));
    }
    
    function WebSocket($address,$port){
      $master=socket_create(AF_INET, SOCK_STREAM, SOL_TCP)     or die("socket_create() failed");
      socket_set_option($master, SOL_SOCKET, SO_REUSEADDR, 1)  or die("socket_option() failed");
      socket_bind($master, $address, $port)                    or die("socket_bind() failed");
      socket_listen($master,20)                                or die("socket_listen() failed");
      echo "Server Started : ".date('Y-m-d H:i:s')."\n";
      echo "Master socket  : ".$master."\n";
      echo "Listening on   : ".$address." port ".$port."\n\n";
      return $master;
    }
    
    function connect($socket){
      global $sockets,$users;
      $user = new User();
      $user->id = uniqid();
      $user->socket = $socket;
      array_push($users,$user);
      array_push($sockets,$socket);
      console($socket." CONNECTED!");
    }
    
    function disconnect($socket){
      global $sockets,$users;
      $found=null;
      $n=count($users);
      for($i=0;$i<$n;$i++){
        if($users[$i]->socket==$socket){ $found=$i; break; }
      }
      if(!is_null($found)){ array_splice($users,$found,1); }
      $index = array_search($socket,$sockets);
      socket_close($socket);
      console($socket." DISCONNECTED!");
      if($index>=0){ array_splice($sockets,$index,1); }
    }
    
    function dohandshake($user,$buffer){
      console("\nRequesting handshake...");
      console($buffer);
      //list($resource,$host,$origin,$strkey1,$strkey2,$data) 
      list($resource,$host,$u,$c,$key,$protocol,$version,$origin,$data) = getheaders($buffer);
      console("Handshaking...");
    
        $acceptkey = base64_encode(sha1($key . "258EAFA5-E914-47DA-95CA-C5AB0DC85B11",true));
      $upgrade  = "HTTP/1.1 101 Switching Protocols\r\nUpgrade: websocket\r\nConnection: Upgrade\r\nSec-WebSocket-Accept: $acceptkey\r\n";
    
      socket_write($user->socket,$upgrade,strlen($upgrade));
      $user->handshake=true;
      console($upgrade);
      console("Done handshaking...");
      return true;
    }
    
    function getheaders($req){
        $r=$h=$u=$c=$key=$protocol=$version=$o=$data=null;
        if(preg_match("/GET (.*) HTTP/"   ,$req,$match)){ $r=$match[1]; }
        if(preg_match("/Host: (.*)\r\n/"  ,$req,$match)){ $h=$match[1]; }
        if(preg_match("/Upgrade: (.*)\r\n/",$req,$match)){ $u=$match[1]; }
        if(preg_match("/Connection: (.*)\r\n/",$req,$match)){ $c=$match[1]; }
        if(preg_match("/Sec-WebSocket-Key: (.*)\r\n/",$req,$match)){ $key=$match[1]; }
        if(preg_match("/Sec-WebSocket-Protocol: (.*)\r\n/",$req,$match)){ $protocol=$match[1]; }
        if(preg_match("/Sec-WebSocket-Version: (.*)\r\n/",$req,$match)){ $version=$match[1]; }
        if(preg_match("/Origin: (.*)\r\n/",$req,$match)){ $o=$match[1]; }
        if(preg_match("/\r\n(.*?)\$/",$req,$match)){ $data=$match[1]; }
        return array($r,$h,$u,$c,$key,$protocol,$version,$o,$data);
    }
    
    function getuserbysocket($socket){
      global $users;
      $found=null;
      foreach($users as $user){
        if($user->socket==$socket){ $found=$user; break; }
      }
      return $found;
    }
    
    function     say($msg=""){ echo $msg."\n"; }
    function    wrap($msg=""){ return chr(0).$msg.chr(255); }
    function  unwrap($msg=""){ return substr($msg,1,strlen($msg)-2); }
    function console($msg=""){ global $debug; if($debug){ echo $msg."\n"; } }
    
    class User{
      var $id;
      var $socket;
      var $handshake;
    }
    
    ?>
    

    and the client:

    var connection = new WebSocket('ws://localhost:12345');
    connection.onopen = function () {
      connection.send('Ping'); // Send the message 'Ping' to the server
    };
    
    // Log errors
    connection.onerror = function (error) {
      console.log('WebSocket Error ' + error);
    };
    
    // Log messages from the server
    connection.onmessage = function (e) {
      console.log('Server: ' + e.data);
    };
    

    If there is anything wrong in my code can you help me fix it? Console in Firefox says:

    Firefox can't establish a connection to the server at ws://localhost:12345/.

  • Dharman
    Dharman over 11 years
    Thx, I'll try do it your way. How do you rate the performance of this PHP server?
  • HartleySan
    HartleySan over 11 years
    @Dharman, it's hard to say because I've only been able to run it on my localhost. Of course it works fine there, but on a real server with a heavy load, I don't know. I imagine it would run fairly well though, as there's no bloat in my code.
  • HartleySan
    HartleySan almost 11 years
    I don't have it up at the moment, but in the near future, I'm planning on writing up a tutorial about the whole process with all the code. Once I do that, I will post the link.
  • forthrin
    forthrin over 10 years
    @HartleySan: Hello! I'd be very interested in looking at your code. Could you put it online, or send it to me personally?
  • HartleySan
    HartleySan over 10 years
    Yes, it will be online soon. Sorry to everyone that has asked for it. I've just been so busy recently. I will get on it soon.
  • Chris
    Chris over 10 years
    This is sockets, not WebSockets. There is a large difference.
  • wolvorinePk
    wolvorinePk over 9 years
    @@HartleySan have you uploaded your code for the public ? if yes then kindly provide your link ?
  • Alcalyn
    Alcalyn almost 8 years
    I add here my solution which uses Ratchet and Silex: github.com/eole-io/sandstone I don't know if you'll find it usefull
  • JayminLimbachiya
    JayminLimbachiya over 5 years
    @techexpander, As per question you have to explain from scratch instead of ex. which is not working.
  • Kiren S
    Kiren S about 5 years
    @HartleySan as you said in last part how can I send message from php server to websocket server? Through HTTP call to wss host?
  • Kiren S
    Kiren S about 5 years
    so we have to use a http client to make http request from php to node server right?
  • HartleySan
    HartleySan about 5 years
    @KirenSiva, I think this guy answers your question pretty well: stackoverflow.com/a/22411059/1992973. With that said, I'm not sure of the use case, but normally, you'll want to send a WebSocket request directly from a browser to a WebSocket server, not from PHP to a WebSocket server. And like I recommended above, Node.js is much better equipped to handle this than PHP, and it's relatively easy to install and set up on any server.
  • M.Z.
    M.Z. about 5 years
    Kiren Siva, correct. Curl or similar, then node broadcasts a message thru websocket
  • Wais Kamal
    Wais Kamal over 3 years
    @HartleySan I have a question and I hope you can answer. You said you wrote your websocket server from scratch. Did you need to use some external library/framework?
  • HartleySan
    HartleySan over 3 years
    @WaisKamal, no, I wrote it using only built-in PHP functions. That said, I did consult existing WebSocket libraries, etc. for guidance and advice. If you want my honest opinion: It's much easier these days to install Node on your server and do WebSocket through Node. Almost all dev environments these days rely on Node anyway, so it's probably already installed. There are a ton of good WebSocket libraries in Node that are lightweight and easy to use. You can be up and running in a couple of hours, probably.
  • evilReiko
    evilReiko over 2 years
    This is good as an introduction of how websockets could work with PHP, but implementation might not be really worth it in real world, as it uses while(true) .. sleep(1) (which is called "long polling", which is not good). On the other hand, Ratchet uses in its core ZeroMQ, which handles it properly.
  • LovelyHanibal
    LovelyHanibal over 2 years
    That is a good example. It nicely and simply show how to listen to connection and send data to webbrowser. I wish there will also add example how to receive data from webrowser to server. When I use socket_read after handsahek data seem seem to be not ASCII.
  • Basj
    Basj over 2 years
    @evilReiko No no this is not long polling at all; the while / sleep(1) is just an example to make the server send example messages every second to the client, just as a demo of WebSocket. In real applications, you don't need at all this sleep(1) on server code!
  • evilReiko
    evilReiko over 2 years
    @Basj oh, you are right!