timostamm/websocket-server

一个简单的非阻塞服务器,专门用于WebSocket。

v3.1.1 2023-06-19 11:48 UTC

This package is auto-updated.

Last update: 2024-09-19 14:28:39 UTC


README

build Packagist PHP Version GitHub tag License

一个简单的非阻塞服务器,专门用于WebSocket。

WebSocket协议实现的功劳归功于ratchet/rfc6455

示例

$loop = Factory::create(); // use a react event loop

// start server
$server = new WebsocketServer($loop, [
    'uri' => '127.0.0.1:23080'
]);

// add a controller 
$server->route([
    'match' => '/example/*',
    'controller' => new class() implements ControllerInterface
    {
        function onOpen(WebSocket $connection): void
        {
            print $connection . ' connected. Sending a "Hello".' . PHP_EOL;
            $connection->send('Hello');
        }

        function onMessage(WebSocket $from, string $payload, bool $binary): void
        {
            print $from . ' sent: ' . $payload . PHP_EOL;
        }

        function onClose(WebSocket $connection, ?Throwable $error): void
        {
            print $connection . ' disconnected.' . PHP_EOL;
        }

    }
]);

// This error handler will be called when an exception was thrown
// by a filter, a controller method or the underlying tcp server.
$server->on('error', function (Throwable $error) {
    print 'Server error: ' . $error->getMessage() . PHP_EOL;
});

$loop->run(); // the react event loop processes socket connections

路由

此路由将匹配以/example/开头的路径。

$server->route([
    'match' => '/example/*',
    'controller' => $controller
]);

占位符使用fnmatch()实现。

此路由将匹配任何路径

$server->route([
    'controller' => $controller
]);

如果客户端没有指定以下子协议之一,则此路由将拒绝WebSocket握手

$server->route([
    'protocols' => ['soap'],
    'controller' => $controller
]);

请求过滤器

此过滤器以HTTP 403响应

$server->filter('example/403', function () {
    throw ResponseException::create(403);
});

此过滤器修改请求

$server->filter('example/add-attribute', function (ServerRequestInterface $request) {
    return $request->withAttribute('X-filter', 'passed');
});

此过滤器只允许指定的源

$server->filter('example/origin', new OriginFilter(['example.com']));

您可以提供自己的RequestMatcherInterface和RequestFilterInterface实现。过滤器也可以通过route()添加

$server->route([
    'match' => '/example/*', 
    'filter' => function(ServerRequestInterface $request){
        if ($request->getRequestTarget() === '/example/forbidden') {
            throw ResponseException::create(403);
        }
    }
    'controller' => ...
]);

身份验证

此库不提供会话集成,但提供支持Bearer令牌身份验证。

通过使用您的令牌验证代码扩展AbstractTokenAuthenticator并返回一个用户对象。用户对象将在请求属性“user”中可用。如果没有令牌,则用户属性将为空。

使用AuthorizationFilter检查用户是否存在。提供$checkUser函数以检查用户是否已授权。

请参阅examples/token-auth.php

Apache配置

将以下内容添加到.htaccess文件中,以将带有Upgrade: websocket头部的所有请求代理到WebSocket服务器。

<IfModule mod_rewrite.c>
    RewriteEngine On
    RewriteCond %{HTTP:Upgrade} =websocket [NC]
    RewriteRule ^(.*)$          ws://127.0.0.1:23080/$1 [P,L]
</IfModule>

JavaScript

var ws = new WebSocket("ws://:23080/hello/foo");
ws.onmessage = function (event) {
    console.log("message", event.data);
};

更多控制器功能

您可以实现以下接口之一或多个,以获取对循环、连接到此控制器的客户端等的访问。

class MyCtrl implements ControllerInterface, LoopAwareInterface ConnectionListAwareInterface, OnShutDownInterface, OnLastCloseInterface, OnFirstOpenInterface
{

    function setLoop(\React\EventLoop\LoopInterface $loop, callable $exceptionHandler): void
    {
        print 'Got loop.' . PHP_EOL;
    }

    function setConnections(\SplObjectStorage $webSockets): void
    {
        print 'Got connection list.' . PHP_EOL;
    }
    
    function onShutDown(): PromiseInterface
    {
         // Will be called when the server is asked to shutdown.
         // Use this hook to finish important tasks, then resolve the promise.
    }

    function onLastClose(WebSocket $socket): void
    {
        print 'Last connection closed.' . PHP_EOL;
    }

    function onFirstOpen(WebSocket $socket): void
    {
        print 'First connection opened.' . PHP_EOL;
    }

    function onOpen(WebSocket $socket): void
    {
        print $socket . ' connected. Sending a "Hello".' . PHP_EOL;
        $socket->send('Hello');
    }

    function onMessage(WebSocket $from, string $payload, bool $binary): void
    {
        print $from . ' received: ' . $payload . PHP_EOL;
    }

    function onClose(WebSocket $socket): void
    {
        print $socket . ' disconnected.' . PHP_EOL;
    }

    function onError(WebSocket $socket, \Throwable $error): void
    {
        print $socket . ' error: ' . $error->getMessage() . PHP_EOL;
    }

}