As far as I know ping/pong exists to solve 3 problems; (1) preventing firewalls from closing the connection, (2) letting the _client_ know that the connection is still open, (3) letting the _server_ know that the connection is still open. Current implementation solves first 2 problems but not the last one. The server can not detect when a connection was ungracefully closed, i.e. because of power loss.
Proposed solution:
Adding a new interface PingInterface.php
<?php
namespace Ratchet;
interface PingInterface {
function onPing(ConnectionInterface $conn);
}
in RFC6455.php after line 92 add:
if ($from->WebSocket->coalescedCallback instanceof PingInterface) {
$from->WebSocket->coalescedCallback->isPingImplemented = true;
}
else {
$from->WebSocket->coalescedCallback->isPingImplemented = false;
}
in RFC6455.php after line 162 add:
if ($from->WebSocket->coalescedCallback->isPingImplemented) {
$from->WebSocket->coalescedCallback->onPing($from);
}
Usage:
My app class will implement PingInterface.
Then in my app class in onOpen and onPing methods I can update some variable with current time:
$conn->lastPingTime = time()
Then I can add a periodic timer to check all connection when they pinged last time and close those connections which stopped pinging. Or I can add a timer to each connection to close the connection after some time and when I receive a ping I will renew the timer.
This change does not break BC.
I believe this is addressed in the upcoming 0.4 release in WsServer
Thanks for the fast reply.
When is 0.4 going to be released?
My code looks like this now:
<?php
namespace MyWebsocketServer;
use \Ratchet\MessageComponentInterface;
use \Ratchet\WebSocket\WsServer;
class CustomWsServer extends WsServer {
public function __construct(MessageComponentInterface $component)
{
parent::__construct($component);
$this->versioner->enableVersion(new CustomRFC6455($this->validator));
}
}
and
<?php
namespace MyWebsocketServer;
use \Ratchet\WebSocket\Version\RFC6455;
class CustomRFC6455 extends RFC6455
{
/**
* @param CustomConnection $from
* @param string $data
*/
public function onMessage(\Ratchet\ConnectionInterface $from, $data)
{
parent::onMessage($from, $data);
if (
$from->WebSocket->coalescedCallback instanceof PingInterface
&& \Ratchet\WebSocket\Version\RFC6455\Frame::OP_PING === (ord($data[0]) & ~0xF0)
&& 0x00 === (ord($data[1]) & 0x7F)
&& 6 === strlen($data)
) {
$from->WebSocket->coalescedCallback->onPing($from);
}
}
}
Do you know whether I can safely remove the last 2 checks && 0x00 === (ord($data[1]) & 0x7F) && 6 === strlen($data) ? I can't do $from->WebSocket->frame->getOpcode(); because the frame is unset in the parent method and I can't check $from->WebSocket->frame->isCoalesced() for the same reason. I mean is it possible to get a random partial frame that starts with 0x89 (aka Frame::OP_PING)? Or is it impossible in non binary mode? I dint want to check the message length because according to RFC6455 : _A Ping frame MAY include "Application data"._
I mean is it possible to get a random partial frame that starts with 0x89 (aka Frame::OP_PING)?
I don't think so as those are reserved bits for control codes.
0.4 will be released when I'm finished the documentation on the website. I've put some time into it recently hope to finish up soon. You should be safe to use the branch even though it hasn't been released yet. I've had reports of many people upgrading with no issues. Unless someone reports an issue in the mean time the current branch will be tagged as-is.
Most helpful comment
I don't think so as those are reserved bits for control codes.
0.4 will be released when I'm finished the documentation on the website. I've put some time into it recently hope to finish up soon. You should be safe to use the branch even though it hasn't been released yet. I've had reports of many people upgrading with no issues. Unless someone reports an issue in the mean time the current branch will be tagged as-is.