varspool/websocket-bundle

PHP WebSocket 客户端/服务器库

dev-master 2015-10-31 18:56 UTC

This package is auto-updated.

Last update: 2024-09-09 23:01:20 UTC


README

VarspoolWebsocketBundle

Alpha稳定性。提供WebSocket服务,包括内置服务器、多路复用、语义配置。

在Symfony2中处理WebSocket可能有更好的方式,其中一些方式可能使您甚至不需要第三方包。例如,使用Ratchet所需的代码量很小,您可能决定放弃自定义配置和使用容器来配置服务器。

安装

VarspoolWebsocketBundle依赖于

当然,还需要Symfony2。大部分,该包是在WebSocket 2.0之上的一个轻量级兼容层,允许它与服务容器一起使用。

配置依赖项

Composer

如果您使用Composer,请将以下包添加到项目的需求中

{
    "require": {
        "varspool/websocket-bundle": "dev-master",
        "wrench/wrench": "dev-master"
    }
}

deps文件

如果您使用bin/vendors来配置依赖项,请将以下行添加到您的deps文件中

[wrench]
    git=git://github.com/varspool/Wrench.git
    version=origin/master

[VarspoolWebsocketBundle]
    git=git://github.com/varspool/WebsocketBundle.git
    target=/bundles/Varspool/WebsocketBundle
    version=origin/master

或者,首先将其分支到自己的存储库,以便您可以发送pull请求并改进上游👍。完成此操作后,您可以使用bin/vendors获取包

$ bin/vendors update

[...]
  > Installing/Updating wrench
  > Installing/Updating VarspoolWebsocketBundle
app/autoload.php

在自动加载器中注册Varspool和Wrench命名空间(如果您使用的是带有Symfony 2.1的Composer则不是必需的)

# app/autoload.php
$loader->registerNamespaces(array(
    // [...]
    'Varspool' => __DIR__.'/../vendor/bundles',
    'Wrench'   => __DIR__.'/../vendor/wrench/lib'
));

app/AppKernel.php

注册VarspoolWebsocketBundle

# app/AppKernel.php
public function registerBundles()
{
    $bundles = array(
        //...
        new Varspool\WebsocketBundle\VarspoolWebsocketBundle(),
    );
}

使用

服务器端以服务器启动

任何PHP进程只能运行一个WebSocket服务器,为可变数量的客户端提供服务。性能未测试,一旦进入生产阶段,您可能需要替换服务器端。(下面的多路复用接口可能会有所帮助:在您的PHP应用程序代码和轻量级WebSocket服务器之间放置一个消息队列。)

要启动服务器,该包提供了一个websocket:listen控制台命令,可以通过app/console访问

Usage:
 websocket:listen [server_name]

Arguments:
 server_name      The server name (from your varspool_websocket configuration) (default: default)

listen命令接受一个必需的参数:要使用的服务器配置名称。服务器定义在您的Symfony2配置中,无论是YAML、XML还是PHP。您必须至少定义一个以开始(我们建议“默认”)。以下是一个定义的例子

varspool_websocket:
    servers:
        default: # Server name
            listen: ws://192.168.1.103:8000 # default: ws://:8000

            # Applications this server will allow
            applications:
                - echo
                - multiplex

            # Origin control
            check_origin: true
            allow_origin: # default: just localhost (not useful!)
                - "example.com"
                - "development.localdomain"

            # Other defaults
            max_clients:             30
            max_connections_per_ip:  5
            max_requests_per_minute: 50

一旦配置了服务器,运行websocket:listen命令。当它运行时,服务器将启动并为您配置中定义的应用程序提供服务。

应用程序

服务器本身不做任何事情,直到您为它编写一个应用程序。一旦应用程序注册,服务器就会调用您应用程序上的方法。

注册您应用程序很容易。服务器寻找标记为varspool_websocket.application的服务。因此,要运行应用程序,导出一个带有该标记的服务。

单个服务器守护进程可以为一到多个应用程序提供服务。因此,您还必须在您的标记中包含一个key属性。这最终会显示在您的应用程序URL中。例如,如果您使用以下服务定义

<!-- Application\ChatBundle\Resources\config\services.xml -->
<service id="chat_service" class="Application\ChatBundle\Services\ChatService">
    <tag name="varspool_websocket.application" key="chat" />
</service>

并且您的服务器配置为监听192.168.1.10:8000,那么您应用程序的URL将是

ws://192.168.1.10:8000/chat

除非在服务器配置中指定,否则不会在服务器上注册应用程序。因此,要启用默认服务器上的上述应用程序,您的配置需要包含

# app/config.yml
varspool_websocket:
    servers:
        default:
            # ...
            applications:
                - chat

以下是一个另一个服务定义的例子,这次是YAML格式

services:
    websocket_example:
        class: Application\ExampleBundle\Application\ExampleApplication
        tags:
            - { name: varspool_websocket.application, key: foobar }

关于一个简单应用的例子,请参阅Application\EchoApplication

我建议您让您的应用程序类继承Varspool\WebsocketBundle\Application\Application,但这不是必须的。会进行一点类型检查,看看您的应用程序是否需要日志记录支持,但仅此而已。所以,您可以自由地扩展任何您喜欢的类:只需实现一个兼容的接口。(这是php-websocket迄今为止采取的方法:提供了一个抽象的WebSocket\Application类,但服务器不进行类型检查。)

最后,这里展示了运行时定义了一些服务后的监听命令的示例

$ app/console websocket:listen default
info: Listening on 192.168.1.103:8080 with ssl off
info: Server created
info: Registering application: test (Application\TestBundle\Application\TestApplication)
info: Registering application: auth (Application\TestBundle\Application\AuthApplication)
info: Registering application: multiplex (Varspool\WebsocketBundle\Application\MultiplexApplication)
info: Registering application: echo (Varspool\WebsocketBundle\Application\EchoApplication)

客户端

当然,您需要一个支持websockets的浏览器。

至于JavaScript库,这主要取决于您。但除非您已经在使用Coffeescript,否则您可能会发现php-websocket附带的一些库安装起来很麻烦。

多路复用

我建议您多路复用您的JavaScript组件的连接。SockJS实现这种方法的https://rabbitmq.cn/blog/2012/02/23/how-to-compose-apps-using-websockets/非常优雅,并且由这个包附带的应用程序支持。

客户端多路复用

这个包与SockJS websocket-multiplex前端库使用的多路复用协议兼容。sockjs/websocket-multiplex提供下载。它们甚至还有一个方便的CDN。

<script src="http://cdn.sockjs.org/websocket-multiplex-0.1.js"></script>

这个JavaScript库提供了一个WebSocketMultiplex对象。您可以给它提供任何与原生的WebSocket兼容的对象。所以,一开始您可以给它提供一个原生的WebSocket,然后,当您决定安装一个SockJS服务器(或者PHP中已经实现了)时,您可以给它提供一个SockJS对象。所以,像这样

var url         = 'ws://example.com:8000/multiplex';

var socket;
if (window.MozWebSocket) {
    socket = new MozWebSocket(url);
} else if (window.WebSocket) {
    socket = new WebSocket(url);
} else {
    throw "No websocket support detected"
}

socket.binaryType = 'blob';

var real_socket = new WebSocket(url);
var multiplexer = new WebSocketMultiplex(real_socket);

var foo  = multiplexer.channel('bar');
// foo.send(), events: open, close, error, message

var logs = mutliplexer.channel('log_server');
// logs.send(), events: open, close, error, message

服务器端多路复用

这个包的默认配置(在Varspool/WebsocketBundle/Resources/config/services.xml中)定义了一个键为"multiplex"的服务器端多路复用应用程序。请确保这个键已列在您的配置中,作为服务器允许的应用程序之一。

当您使用由服务器运行的multiplex应用程序时,您的套接字被进一步抽象为channels,由一个topic字符串标识。所有服务器端的channel监听器都会通知来自客户端的每个消息。监听器可以决定仅回复发送消息的客户端,或者回复订阅了该channel的所有客户端。

  • 客户端不能向其他客户端发送消息,除非您明确地中继它们。
    • Channel提供了一个方便的抽象来做这件事:$channel->send('foo', 'text', false, array('except' => $client))
  • 监听器不能向其他监听器发送消息。
    • 但您可以使用任何喜欢的方法:监听器可以被注入到服务容器中。
Listener/ConnectionListener接口

在服务器端,您只需要实现Multiplex\Listener,以便能够监听channel上的事件

/**
 * @param Channel $channel   The channel is an object that holds all the active
 *          client connections to a given topic, and all the server-side
 *          subscribers. You can ->send($message) to the channel to broadcast
 *          it to all the subscribed client connections. ->getTopic() identifies
 *          the topic the message was received on.
 *
 * @param string $message    The received message, as a string
 *
 * @param Connection $client The client connection the message was received
 *          from. You can ->send($string) to the client, but it is a raw Websocket
 *          connection, so if you want to send a multiplexed message to a single
 *          client, you'll probably use
 *          `Varspool\WebsocketBundle\Multiplex\Protocol::toString($type, $topic, $payload)`
 *          and the Protocol::TYPE_MESSAGE constant.
 */
public function onMessage(Channel $channel, $message, Connection $client);

然后只需为您的服务添加varspool_websocket.multiplex_listener标签和您想监听的topic

<service id="example.custom" class="Application\ExampleBundle\Services\CustomService">
    <tag name="varspool_websocket.multiplex_listener" topic="chat" />
</service>

完成了。您的onMessage方法将被调用,并提供客户端发送到您指定的multiplex topic的消息的详细信息。您可能还希望做一些事情,当客户端连接到(实际上,订阅)和从您的服务断开连接(无论是真正的断开连接还是取消订阅)时。为此,实现额外的Multiplex\ConnectionListener接口。

use Varspool\WebsocketBundle\Multiplex\Listener;
use Varspool\WebsocketBundle\Multiplex\ConnectionListener;
use Varspool\WebsocketBundle\Multiplex\Channel;
use Wrench\Connection;

class GameServer implements Listener, ConnectionListener
{
    public function onMessage(Channel $channel, $message, Connection $client)
    {
        $client->send('Hello, player!');
        $channel->send('Oh, wow, guys...' . $client->getClientId() . ' is here';
    }

    public function onConnect(Channel $channel, Connection $client)
    {
        $client->send('Welcome to the dungeon');
    }

    public function onDisconnect(Channel $channel, Connection $client)
    {
        $channel->send($client->getClientId() . ' is leaving! OH NOES!');
    }
}
MultiplexService父服务

为了方便起见,如果您想实现这两个接口以及一些其他有用的功能(例如获取服务器或mulitplex应用程序实例的访问权限),只需扩展Services\MultiplexService,并在您的配置中将其导出为varspool_websocket.multiplex_service的父服务。

这是在YAML中的样子(额外:多个channel监听器)

# config.yml
services:
    example.websocket_auth:
        class:  Application\ExampleBundle\Services\AuthService
        parent: varspool_websocket.multiplex_service
        tags:
            -
                name:  varspool_websocket.multiplex_listener
                topic: auth
            -
                name:  varspool_websocket.multiplex_listener
                topic: login