我使用PHP Ratchet构建了一个聊天应用程序。
我将所有连接存储在SplObjectStorage中。
每个连接都有用户ID,我将通过此附加他:
public function __construct() {
$this->clients = new \SplObjectStorage;
}
public function onOpen(ConnectionInterface $conn)
{
// Store the new connection to send messages to later
$querystring = $conn->WebSocket->request->getQuery();
foreach ($querystring as $value)
{
if($key == "senderId")
$senderId = $value;
}
$this->clients->attach($conn, $senderId);
echo "New connection! ({$conn->resourceId}) senderId({$senderId})\n";
}
当邮件到达时,我希望以最快的方式获取与特定用户ID相关的$conn
对象。
我可以像这样使用琐碎的foreach:
public function onMessage(ConnectionInterface $from, $msg) {
$numRecv = count($this->clients) - 1;
echo sprintf('Connection %d sending message "%s" to %d other connection%s' . "\n"
, $from->resourceId, $msg, $numRecv, $numRecv == 1 ? '' : 's');
foreach ($this->clients as $client)
{
if ($from->getInfo() !== $client->getInfo()) {
// do stuff
}
}
我想知道是否有更快的方法。也许使用这样的函数:
$conn = $this->clients->getClientWithInfo("WANTED-INFO");
想要的方法是在我的所有连接上取消循环,以便向特定用户发送消息。 我想获得与用户ID相关联的连接。
答案 0 :(得分:3)
在我看来,只有一种解决方案可以让它发挥作用,就像你期望的那样=>扩展SplObjectStorage类。但是你有两个选择。
首先,你可以懒惰,并为找到对象的类添加一个getClientWithInfo方法:
class ConnectionStorageSimple extends SplObjectStorage
{
public function getClientWithInfo($info)
{
$this->rewind();
while ($this->valid()) {
$object = $this->current(); // similar to current($s)
$data = $this->getInfo();
if ($info === $data) {
$this->rewind();
return $object;
}
$this->next();
}
return null;
}
}
$conStorage = new ConnectionStorageSimple();
$con1 = new \stdClass();
$con1->id = 1;
$con2 = new \stdClass();
$con2->id = 2;
$conStorage->attach($con1, 1);
$conStorage->attach($con2, 2);
var_dump($conStorage->getClientWithInfo(1));
var_dump($conStorage->getClientWithInfo(2));
/**
This will output something like that:
class stdClass#2 (1) {
public $id =>
int(1)
}
class stdClass#3 (1) {
public $id =>
int(2)
}
*/
另一个选择是,您基于父函数构建一个信息对象映射。这有点复杂:
<?php
class ConnectionStorage extends SplObjectStorage
{
private $objInfoMapping = array();
public function attach($object, $data = null)
{
if (null !== $data) {
$this->objInfoMapping[$data] = $object;
}
parent::attach($object, $data);
}
public function detach($object)
{
$this->detach($object);
parent::detach($object);
}
public function addAll($storage)
{
$this->addStorage($storage);
parent::addAll($storage);
}
public function removeAll($storage)
{
$this->objInfoMapping = array();
parent::removeAll($storage);
}
public function removeAllExcept($storage)
{
$this->objInfoMapping = array();
$this->addStorage($storage);
parent::removeAllExcept($storage);
}
public function unserialize($serialized)
{
parent::unserialize($serialized);
$this->addStorage($this);
}
public function offsetUnset($object)
{
$this->detach($object);
parent::offsetUnset($object);
}
protected function detachObject($obj)
{
$info = $this[$obj];
if (array_key_exists($info, $this->objInfoMapping)) {
unset($this->objInfoMapping[$info]);
}
}
protected function addStorage(SplObjectStorage $storage)
{
$storage->rewind();
while ($storage->valid()) {
$object = $storage->current(); // similar to current($s)
$data = $storage->getInfo();
$this->objInfoMapping[$data] = $object;
$storage->next();
}
}
public function getClientWithInfo($info)
{
if (array_key_exists($info, $this->objInfoMapping)) {
return $this->objInfoMapping[$info];
}
}
}
$conStorage = new ConnectionStorage();
$con1 = new \stdClass();
$con1->id = 1;
$con2 = new \stdClass();
$con2->id = 2;
$conStorage->attach($con1, 1);
$conStorage->attach($con2, 2);
var_dump($conStorage->getClientWithInfo(1));
var_dump($conStorage->getClientWithInfo(2));
/**
This will also output something like that:
class stdClass#2 (1) {
public $id =>
int(1)
}
class stdClass#3 (1) {
public $id =>
int(2)
}
*/
两个类之间的主要区别在于,第二个示例在大数据集上的性能会更好,因为您不必遍历存储的所有对象。 而且因为你只存储对自己数组的对象引用,所以额外的内存消耗不应该那么大。
免责声明:这些课程只是为了说明可能性。第一个应该保存使用,但第二个应该进行更多测试
希望这有帮助。
答案 1 :(得分:0)
这就是我所做的,看到它更简单快速。
namespace mine;
use Ratchet\MessageComponentInterface;
use Ratchet\ConnectionInterface;
class ws implements MessageComponentInterface {
protected $clients;
protected $clientids;
public function __construct() {
$this->clients = new \SplObjectStorage;
$this->clientids = array();
}
public function multicast($msg) {
foreach ($this->clients as $client) $client->send($msg);
}
public function send_to($to,$msg) {
if (array_key_exists($to, $this->clientids)) $this->clientids[$to]->send($msg);
}
public function onOpen(ConnectionInterface $conn) {
$socket_name = "{$conn->resourceId}@{$conn->WebSocket->request->getHeader('X-Forwarded-For')}";
$this->clients->attach($conn,$socket_name);
$this->clientids[$socket_name] = $conn;
}
public function onMessage(ConnectionInterface $from, $msg) {
}
public function onClose(ConnectionInterface $conn) {
unset($this->clientids[$this->clients[$conn]]);
$this->clients->detach($conn);
}
public function onError(ConnectionInterface $conn, \Exception $e) {
$conn->close();
}
}
这增加了2个函数,一个用于多播,另一个用于通过socket_name向客户端发送消息,这是一个字符串ID(我选择了套接字ID和ip的组合来阻止可能的冲突)。
以便发送给客户:
$ws->send_to(socket_name,message);
显然$ ws是在initization创建的websocket:
$ws = new mine\ws();
$ws_server = new Ratchet\Server\IoServer( new Ratchet\Http\HttpServer( new Ratchet\WebSocket\WsServer( $ws ) ), $socket );