gomoob/php-websocket-server

带有标签管理的WebSocket服务器,轻松转发消息到正确的客户端!

1.2.0 2016-08-22 21:56 UTC

This package is not auto-updated.

Last update: 2024-09-13 22:40:53 UTC


README

带有标签管理的WebSocket服务器,轻松转发消息到正确的客户端!

Total Downloads Latest Stable Version Build Status Coverage Code Climate License

简介

Gomoob WebSocket服务器是一个简单的Ratchet服务器,它使用自定义标签轻松地将消息转发到客户端。

例如,假设我们有一个包含英语和法语用户的Web应用程序。英语用户应收到英语消息,法语用户应收到法语消息。

每个应用程序都会打开一个具有特定language标签的WebSocket。

// Web Application in English mode
var enWebSocket = new WebSocket('ws://localhost:8080?tags={"language":"EN}');

...

// Web Application in French mode
var frWebSocket = new WebSocket('ws://localhost:8080?tags={"language":"FR}');

...

在服务器端,Gomoob WebSocket服务器会跟踪标签与WebSocket连接之间的关联。例如,以下简单的PHP代码段允许轻松地将消息转发到所有连接到language=FR标签的客户端。

// PHP Server (in most cases a Web Server) to Web Socket server client, allows to send one message which is forwared to
// several opened WebSocket connections
$phpClient = new WebSocketClient('ws://localhost:8080');
$phpClient->send(WebSocketRequest::create($message, ['language' => 'FR']);

安装

服务器端(运行服务器)

运行服务器只需要一行代码。

require __DIR__ . '/vendor/autoload.php';

echo "WebSocket server started, enter Ctrl+C to stop server." . PHP_EOL;
\Gomoob\WebSocket\Server\WebSocketServer::factory()->run();

客户端(PHP)

首先使用以下依赖项使用composer拉取项目。

{
    "require": {
        "gomoob/php-websocket-server": "^1.0.0"
    }
}

然后只需使用\Gomoob\WebSocket\Client\WebSocketClient类来发送您的消息。

// Open a Server / Server WebSocket connection
$phpClient = new WebSocketClient('ws://localhost:8080');

// Forward a message to all the WebSocket client connections associated to 'tag1' and 'tag2'
$response = $phpClient->send(
    WebSocketRequest::create(
        $message, 
        [
            'tag1' => 'tag1Value',
            'tag2' => 'tag2Value'
        ]
    )
);

如果您想编写可靠的单元测试,我们还提供了\Gomoob\WebSocket\Client\WebSocketClientMock类。这个类是一个易于使用的实用工具模拟。

// Somewhere in our code we use a \Gomoob\WebSocket\IWebSocketClient ...
// We suppose this code is implemented in MyPowerfulService->serviceMethod();
$phpClient->send(WebSocketRequest::create('Message 0.')->setTags(['tag0' => 'tag0Value']));
$phpClient->send(WebSocketRequest::create('Message 1.')->setTags(['tag1' => 'tag0Value']));
$phpClient->send(WebSocketRequest::create('Message 2.')->setTags(['tag0' => 'tag0Value', 'tag1' => 'tag1Value']));
		
// Then we write a test case by replacing the real WebSocket client implementation with the mock one
class SampleTestCase extends TestCase
{
    public function setUp() {
        $this->webSocketClient = new WebSocketClientMock();
        $this->myPowerfulService->setWebSocketClient($this->webSocketClient);
    }

    public function testServiceMethod() {
    
        // Calls the method to test
        $this->myPowerfulService->serviceMethod();
    
        // Checks the right requests were sent
        $webSocketRequests = $this->webSocketClient->findByTags(['tag0' => 'tag0Value']);
		$this->assertCount(2, $webSocketRequests);
		$this->assertContains($webSocketRequest0, $webSocketRequests);
		$this->assertNotContains($webSocketRequest1, $webSocketRequests);
		$this->assertContains($webSocketRequest2, $webSocketRequests);
    }
}

高级配置

默认情况下,Gomoob WebSocket服务器的行为如下!

  • 公开端口8080并授权来自所有IP地址的连接(即0.0.0.0);
  • 仅接受纯字符串消息(如果发送/接收JSON消息,则会遇到异常);
  • 使用默认的PSR记录器,将消息输出到终端;
  • 不管理任何授权检查。

如果这些行为中任何一个不符合您的需求,请阅读以下子部分。您还可以阅读src/test/server.php文件,该文件展示了如何以自定义消息解析和授权启动服务器。

消息解析器

默认情况下,WebSocket服务器将接受纯字符串消息,如果您尝试发送JSON对象,则会遇到以下异常。

The 'message' property is not a string, you must configure a message parser to parse messages !

这是预期行为,如果您希望服务器管理自定义PHP对象消息,则必须

  • 使您的PHP对象消息扩展\JsonSerializable并正确实现jsonSerialize()方法;
  • 实现一个自定义消息解析器以在接收到纯JSON对象时创建您的自定义PHP对象消息。

\Gomoob\WebSocket\Message\Message类中提供了一个示例消息对象,您可以自由阅读相关的源代码以了解其工作原理。您还可以在\Gomoob\WebSocket\Message\MessageParser中找到一个示例消息解析器。

为了说明如何管理自定义PHP对象消息,让我们假设我们有一个以下消息对象要发送。

class MyMessage {
    private $messageProperty1;
    public function __construct($messageProperty1) {
        $this->messageProperty1 = $messageProperty1; 
    }
    public function getMessageProperty1() {
        return $this->messageProperty1;
    }
}

在浏览器上的JavaScript中发送此类消息需要以下代码。

var socket = new WebSocket('ws://localhost:8080');
socket.send(
    JSON.stringify(
        {
            message : {
                messageProperty1 : "Hello !"
            },
            tags : {
                tag1 : 'tag1Value'
            }
        }
    )
);

或者使用我们提供的客户端在PHP中。

WebSocketClient::factory('ws://localhost:8080')->send(WebSocketRequest::create(new MyMessage('Hello !')));

这将不起作用,因为服务器端的Gomoob WebSocket服务器将不知道如何解析消息以及如何重新创建这些消息以转发给已打开WebSocket连接的客户端。

首先,我们必须在我们的MyMessage类中实现\JsonSerializable类和jsonSerializeMethod()

class MyMessage implements \JsonSerializable {
   ...
   public function jsonSerialize() {
       return [
           'messageProperty1' => $this->messageProperty1;
        ];
   }
}

然后我们必须通过扩展\Gomoob\WebSocket\IMessageParser类来实现消息解析器。

use Gomoob\WebSocket\IMessageParser;

class MyMessageParser implement IMessageParser {
    public function parse(array $arrayMessage)
    {
        // Ensure the array contains only valid key names
        foreach (array_keys($arrayMessage) as $key) {
            if (!is_string($key) || !in_array($key, ['messageProperty1'])) {
                throw new \InvalidArgumentException('Unexpected property \'' . $key . '\' !');
            }
        }
        
        // The 'messageProperty1' property is mandatory
        if (!array_key_exists('messageProperty1', $arrayMessage)) {
            throw new \InvalidArgumentException('No \'messageProperty1\' property found !');
        }
        
        return new MyMessage($arrayMessage['messageProperty1']);
    }
}

最后,我们必须在创建WebSocket服务器时提供我们的解析器。

WebSocketServer::factory(
    [
    	'messageParser' => new MyMessageParser()
    ]
)->run();

授权管理器

默认情况下,WebSocket服务器将接受所有连接和消息发送。在大多数情况下,这种行为是不期望的,因为任何人都可以在你的服务器上打开WebSocket,并尝试未经授权将消息转发给所有已连接客户端。

您可以通过实现\Gomoob\WebSocket\IAuthManager接口来创建自定义授权管理器,该接口具有以下签名。

/**
 * Interface which defines an authorization manager. An authorization manager allows to control authorization while
 * opening Web Socket connections and sending messages over Web Sockets.
 *
 * @author Baptiste Gaillard (baptiste.gaillard@gomoob.com)
 */
interface IAuthManager
{
    /**
     * Function used to indicate if connection opening is authorized.
     *
     * @param \Ratchet\ConnectionInterface $connection the current Ratchet connection.
     *
     * @return boolean `true` if the connection opening is authorized, `false` otherwise.
     */
    public function authorizeOpen(ConnectionInterface $connection);

    /**
     * Function used to indicate if message sending is authorized.
     *
     * @param \Ratchet\ConnectionInterface $connection the current Ratchet connection.
     * @param \Gomoob\WebSocket\IWebSocketRequest $webSocketRequest the current Gomoob WebSocket request.
     */
    public function authorizeSend(ConnectionInterface $connection, IWebSocketRequest $webSocketRequest);
}

因此,管理授权非常简单,只需使用authorizeOpen(...)authorizeSend(...)函数返回truefalse即可。

ApplicationsAuthManager

为了简化授权,我们提供了一个授权管理器,允许声明具有keysecret属性的多个应用程序。

此授权管理器位于\Gomoob\WebSocket\Auth\ApplicationsAuthManager类中,它使用一个非常简单的YAML配置文件。

以下是使用WebSocket服务器实例化该管理器的示例。

WebSocketServer::factory(
    [
    	'authManager' => ApplicationsAuthManager::factory(
    	    [
    	        'authorizeOpen' => false,
    	    	'configurationFile' => __DIR__ . '/auth.yml'
    	    ]
    	)
    ]
)->run();

auth.yml文件的内容可以是以下内容。

applications:
  - 
    key: application1
    secret: B4ajW3P7jfWEYPZsQV8mnteHg97G67uW
    authorizeOpen: true
  - key: application2
    secret: 33yLWdynhaqm9tYjDFKf8gB8zmAPKdDP
    authorizeOpen: false

然后以下JavaScript代码将应用。

// Does not work because required 'key' and 'secret' URL parameters are not provided
var socket1 = new WebSocket('wss://myserver.org:8080'); 

// Works because the 'key' and 'secret' URL parameters provided are valid
var socket2 = new WebSocket('wss://myserver.ord:8080?key=application1&secret=B4ajW3P7jfWEYPZsQV8mnteHg97G67uW');

// Does not work because the request does not provide the 'key' and 'secret' properties
socket2.send(
    JSON.stringify(
        {
            message : {
                messageProperty1 : "Hello !"
            }
        }
    )
);

// Works because the request provides valid 'key' and 'secret' properties
socket2.send(
    JSON.stringify(
        {
            message : {
                messageProperty1 : "Hello !"
            },
            metadata : {
                key : 'application2',
                secret : '33yLWdynhaqm9tYjDFKf8gB8zmAPKdDP'
            }
        }
    )
);

相同的规则也适用于我们提供的PHP客户端。

WebSocketClient::factory('ws://localhost:8080')->send(
    WebSocketRequest::create(
        new MyMessage('Hello !')
    )->setMetadata(
        [
            'key' => 'application2',
            'secret' => '33yLWdynhaqm9tYjDFKf8gB8zmAPKdDP'
        ]
    )
);

Docker容器

为了帮助您快速启动,我们还提供了以下Docker容器:https://hub.docker.com/r/gomoob/php-websocket-server

发行历史

1.2.0 (2016-08-23)

  • TagsTree类移动到\Gomoob\WebSocket\Util\TagsTree
  • 添加新的TagsTree->reset()方法;
  • 添加新的\Gomoob\WebSocket\Client\WebSocketClientMock类以简化单元测试;
  • 更新composer依赖项。

1.1.0 (2016-08-18)

  • \Gomoob\WebSocket\IWebSocketRequest接口和\Gomoob\WebSocket\Request\WebSocketRequest类中添加更多PHP Documentor文档,关于metadata的目标;
  • \Gomoob\WebSoscket\IWebSocketClient接口和\Gomoob\WebSocket\Client\WebSocketClient类中添加对defaultMetadata的管理;
  • \Gomoob\WebSocket\IWebSocketClient接口和\Gomoob\WebSocket\Client\WebSocketClient类中添加对defaultTags的管理;
  • 改进\Gomoob\WebSocket\Message\Message序列化;
  • 改进\Gomoob\WebSocket\Request\WebSocketRequest序列化;
  • 现在所有工厂方法都可以通过factory(...)方法或别名create(...)方法调用。

1.0.3 (2016-08-17)

  • 修复创建WebSocketServerportaddress选项的问题,参数未传递给Ratchet服务器;
  • 现在默认端口号是80,这是Ratchet服务器的默认端口号。

1.0.2 (2016-08-17)

  • 添加缺少的symfony/yaml composer依赖项,否则在运行composer update --no-dev时遇到问题;
  • 添加缺少的monolog/monolog composer依赖项,否则在运行composer update --no-dev时遇到问题。

1.0.1 (2016-08-17)

  • 配置特定的Eclipse验证规则;
  • 添加MIT许可证。

1.0.0 (2016-08-17)

  • 第一个版本。

关于Gomoob

Gomoob,我们每天都在使用惊人的开源框架构建高质量的软件。您想与我们开始下一个项目吗?那太好了!给我们打电话或给我们发电子邮件,我们将尽快回复您!

您可以通过电子邮件contact@gomoob.com或电话号码(+33)6 85 12 81 26或(+33)6 28 35 04 49与我们联系。

还可以访问http://gomoob.github.io以了解更多我们开发的开源软件。