Simple websocket server in PHP
Asked Answered
S

2

16

I am developing a simple websocket server in PHP. I know there are quite a few existing implementations but I want to make my own so to learn the protocol better. I managed to do the handshaking fine and my clients connect to the server. I also managed to decode the data from the client but I have problems sending back messages. The client disconnects when it receives my response. Firefox says The connection to ws://localhost:12345/ was interrupted while the page was loading..

I used this answer as a guide.

Here is my code for wrapping the data:

private function wrap($msg = ""){
    $length = strlen($msg);
    $this->log("wrapping (" . $length . " bytes): " . $msg);

    $bytesFormatted = chr(129);
    if($length <= 125){
        $bytesFormatted .= chr($length);
    } else if($length >= 126 && $length <= 65535) {
        $bytesFormatted .= chr(126);
        $bytesFormatted .= chr(( $length  >> 8 ) & 255);
        $bytesFormatted .= chr(( $length       ) & 255);
    } else {
        $bytesFormatted .= chr(127);
        $bytesFormatted .= chr(( $length >> 56 ) & 255);
        $bytesFormatted .= chr(( $length >> 48 ) & 255);
        $bytesFormatted .= chr(( $length >> 40 ) & 255);
        $bytesFormatted .= chr(( $length >> 32 ) & 255);
        $bytesFormatted .= chr(( $length >> 24 ) & 255);
        $bytesFormatted .= chr(( $length >> 16 ) & 255);
        $bytesFormatted .= chr(( $length >>  8 ) & 255);
        $bytesFormatted .= chr(( $length       ) & 255);
    }

    $bytesFormatted .= $msg;
    $this->log("wrapped (" . strlen($bytesFormatted) . " bytes): " . $bytesFormatted);
    return $bytesFormatted;
}

UPDATE: I tried it with Chrome and I got the following error, printed in the console: A server must not mask any frames that it sends to the client.

I put some console printouts on the server. It is a basic echo server. I try with aaaa. So the actual wrapped message must be 6 bytes. Right?

enter image description here

Chrome prints the above error. Note also that after wrapping the message I simply write it to the socket:

$sent = socket_write($client, $bytesFormatted, strlen($bytesFormatted));
$this->say("! " . $sent);

It prints 6 meaning 6 bytes are actually written to the wire.

If I try with aaa, Chrome doesn't print the error but doesn't call my onmessage handler either. It hangs as if waiting for more data.

Any help highly appreciated. Thanks.

Severalty answered 29/10, 2012 at 11:35 Comment(11)
Have you used wireshark to check that the bytes being sent over the wire match what appears to be generated in the code here? And that no other bit of code is sending additional data which might be interpreted as the start of a message?Snob
@Snob It is on the same machine so I think wireshark can't see the packages. I will try to sniff it somehow. Thanks.Severalty
Do you have a second machine you could temporarily run Firefox on?Snob
Why do you use the chr function and not the pack function?Testosterone
@DaveKok chr(129) returns one byte. pack('c', 129) also returns one byte. See codepad.org/N3KWIXTqSeveralty
Even if your wrapping code is correct, there is much more places where an error can occur, for example (and not limited to) in the way how you apply mask/unmask (which you mentioned as a part of error message from Chrome, and I don't see any masking at all, supposing you try to implement Hybi). As the protocol is not so small, you seem to be in need to post significantly larger portion of code, but it will be harder to proofread by the others. Why don't you think you can learn the protocol better by existing verified sources?Aideaidedecamp
@Aideaidedecamp Hmm, I am following the advice from link in the question where the author says there is no need to mask the output. The input from the browser is always masked but not the output of the server. Also there is no much more code after the wrapping. I simply write the data with socket_write. Its return value indicates to me that all bytes are properly sent to the client.Severalty
Still, the error seems somewhere else, not in the presented portions of the code. From the error message you mentioned, it looks like you're sending some excessive data after handshaking.Aideaidedecamp
@Aideaidedecamp I don't think the problem is in the handshaking. The browser stays connected after the handshake and doesn't report any errors. Also there are no logs on the server. At the moment I try to send something longer than 3 bytes to the server, the browser prints the error and disconnects.Severalty
wireshark/tcpdump can listen on localhost. sudo tcpdump -i lo. assuming this is a linux box. I'm assume there is an equivalent on windows with winpcap, but you will need to check the googles for that.Pacification
@Pacification Actually I used a tool called RawCap and I see all data going to the wire.Severalty
G
4

I had the same problem: for some messages sent from the server there was no response in the browser, for some the error "A server must not mask any frames ..." was displayed, though I did not add any mask. The reason was in the handshake sent. The handshake was:

"HTTP/1.1 101 Web Socket Protocol Handshake\r\n" .
...
"WebSocket-Location: ws://{$host}{$resource}\r\n\r\n" . chr(0)

That chr(0) was the reason, after I removed it everything works.

Goldia answered 8/11, 2012 at 18:55 Comment(0)
M
0

When I wrote my websocket classes, I had the same issue. In my case, I used output buffering to determine that I was echo'ing something out before I sent the reply. Might try that and see if it's the problem.

Micco answered 8/11, 2012 at 0:0 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.