Reputation: 29270
I have a very strange issue. I have an open WebSocket, the communication works perfectly. I also have onblur and onfocus events for the window, which notify the server via said connection. However, in that case, the strings I receive are non-null-terminated. The communication works absolutely impeccably otherwise, even when sending the same strings that are sent on those respective blur-/focus-events. Why is that, and how can it be fixed?
Here's some code:
$(document).ready(function(){
initializeEverything();
window.onblur = function(){ notifyFocusChange(false); };
window.onfocus = function(){ notifyFocusChange(true); };
});
function notifyFocusChange(present){
if(present){
webSocket.send('presence:present');
}else{
webSocket.send('presence:absent');
}
}
And here's an example of a non-null-terminated string I receive when the events fire:
presence:absentÏ┘ê÷/à°äJ÷ÝÿLÓ▓ùM÷Ýÿ[
EDIT: It's been suggested that it may be a server error, so here's the code for decoding incoming messages:
private function decode($payload) {
$length = ord($payload[1]) & 127;
if ($length == 126) {
$masks = substr($payload, 4, 4);
$data = substr($payload, 8);
} elseif ($length == 127) {
$masks = substr($payload, 10, 4);
$data = substr($payload, 14);
} else {
$masks = substr($payload, 2, 4);
$data = substr($payload, 6);
}
$text = '';
for ($i = 0; $i < strlen($data); ++$i) {
$text .= $data[$i] ^ $masks[$i % 4];
}
return $text;
}
EDIT: It only happens in Chrome. I checked it on Firefox, which uses the same WebSocket-protocol, and everything works perfectly fine there.
Upvotes: 1
Views: 1671
Reputation: 73119
The onblur and onfocus events are probably happening several times in rapid succession. Meaning that the websocket.send call is also being called quickly multiple times in rapid succession.
If the multiple send calls happen close enough together then there is a high probability that you will receive multiple frames in a single read at the server and I believe that's what you are seeing. In other words, the "garbage" in your payload is actually one or more subsequent WebSocket frames.
You might be able to replicate the problem manually be doing two separate sends in the same Javascript context:
function do_test () {
ws.send("data1");
ws.send("data2");
}
That will send two WebSocket frames close enough together that the server will probably read them all at once from the socket.
In order to properly process frames you have to parse the payload length field and only read/unmask that specified amount of payload data. Anything remaining needs to be queued up as the beginning of a new frame.
Also, not only can multiple Websocket frames be returned from one read from the socket, but you can't rely on frames being read as whole frames: you might read half the frame in one read and the other half in the next frame. In other words, the first time you read from the socket you might get 3 whole websocket frames plus 1 byte of the 4th frame and then in the next read from the socket you get the rest of the 4th frame.
This can generally be summed up this way: WebSockets are a message based transport protocol, while TCP sockets are a streaming protocol. Since WebSockets are layered on TCP, this means that the WebSocket server (and client for that matter) must do the translation to present whole messages to the application even though the underlying transport is not message based.
Upvotes: 2