icicleio/websocket

Icicle 的异步 WebSocket 组件。

v0.1.0 2016-03-11 17:08 UTC

This package is auto-updated.

Last update: 2024-09-14 02:40:18 UTC


README

异步、非阻塞 WebSocket 服务器。

该库是 Icicle 的组件,提供异步 WebSocket 服务器,可以处理同一端口的常规 HTTP 请求和 WebSocket 请求。与其他 Icicle 组件类似,此库使用从 AwaitablesGenerators 构建的 Coroutines,使得编写异步代码更像编写同步代码。

Build Status Coverage Status Semantic Version MIT License @icicleio on Twitter

文档和支持

要求
  • PHP 5.5+ 对 v0.1.x 分支(当前稳定)和 v1.x 分支(镜像当前稳定)
  • PHP 7 对 v2.0(master)分支支持生成器委托和返回表达式
建议
安装

推荐的安装方式是使用 Composer 包管理器。(有关安装和使用的更多信息,请参阅 Composer 安装指南。)

运行以下命令以在项目中使用此库

composer require icicleio/websocket

您也可以手动编辑 composer.json 以将此库作为项目要求添加。

// composer.json
{
    "require": {
        "icicleio/websocket": "^0.1"
    }
}

示例

以下示例创建了一个简单的 HTTP 服务器,它接受在路径 /echo 上的 WebSocket 连接,否则在其他路径上以 404 状态码响应。

#!/usr/bin/env php
<?php

require '/vendor/autoload.php';

use Icicle\Http\Message\{BasicResponse, Request, Response};
use Icicle\Http\Server\RequestHandler;
use Icicle\Loop;
use Icicle\Socket\Socket;
use Icicle\WebSocket\{Application, Connection, Server\Server};

$echo = new class implements Application {
    /**
     * {@inheritdoc}
     */
    public function onHandshake(Response $response, Request $request, Socket $socket): Response
    {
        // This method provides an opportunity to inspect the Request and Response before a connection is accepted.
        // Cookies may be set and returned on a new Response object, e.g.: return $response->withCookie(...);

        return $response; // No modification needed to the response, so the passed Response object is simply returned.
    }

    /**
     * {@inheritdoc}
     */
    public function onConnection(Connection $connection, Response $response, Request $request): Generator
    {
        // The Response and Request objects used to initiate the connection are provided for informational purposes.
        // This method will primarily interact with the Connection object.

        yield from $connection->send('Connected to echo WebSocket server powered by Icicle.');

        // Messages are read through an Observable that represents an asynchronous set. There are a variety of ways
        // to use this asynchronous set, including an asynchronous iterator as shown in the example below.

        $observable = $connection->read(); // Connection::read() returns an observable of received Message objects.
        $iterator = $observable->getIterator();

        while (yield from $iterator->isValid()) {
            /** @var \Icicle\WebSocket\Message $message */
            $message = $iterator->getCurrent();

            if ($message->getData() === 'close') {
                yield from $connection->close(); // Close the connection if the message contains only 'close'
            } else {
                yield from $connection->send($message); // Echo the message back to the client.
            }
        }

        /** @var \Icicle\WebSocket\Close $close */
        $close = $iterator->getReturn(); // Only needs to be called if the close reason is needed.
    }
};

$server = new Server(new class ($echo) implements RequestHandler {
    /** @var \Icicle\WebSocket\Application */
    private $application;

    public function __construct(Application $application)
    {
        $this->application = $application;
    }

    public function onRequest(Request $request, Socket $socket): Generator
    {
        if ($request->getUri()->getPath() === '/echo') {
            return $this->application; // Return a WebSocket Application to create a WebSocket connection.
        }

        $response = new BasicResponse(Response::NOT_FOUND, [
            'Content-Type' => 'text/plain',
        ]);

        yield from $response->getBody()->end('Resource not found.');

        return $response; // Return a regular HTTP response for other request paths.
    }

    public function onError(int $code, Socket $socket): Response
    {
        return new BasicResponse($code);
    }
});

$server->listen(8080);

Loop\run();