hyperf/docs/zh-hk/websocket-server.md
沈唁 bb3ed4815c
Add translate docs action (#3276)
* Add translate docs action

* Remove push

Co-authored-by: sy-records <sy-records@users.noreply.github.com>
2021-02-19 12:53:50 +08:00

226 lines
6.0 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# WebSocket 服務
Hyperf 提供了對 WebSocket Server 的封裝,可基於 [hyperf/websocket-server](https://github.com/hyperf/websocket-server) 組件快速搭建一個 WebSocket 應用。
## 安裝
```bash
composer require hyperf/websocket-server
```
## 配置 Server
修改 `config/autoload/server.php`,增加以下配置。
```php
<?php
'servers' => [
[
'name' => 'ws',
'type' => Server::SERVER_WEBSOCKET,
'host' => '0.0.0.0',
'port' => 9502,
'sock_type' => SWOOLE_SOCK_TCP,
'callbacks' => [
Event::ON_HAND_SHAKE => [Hyperf\WebSocketServer\Server::class, 'onHandShake'],
Event::ON_MESSAGE => [Hyperf\WebSocketServer\Server::class, 'onMessage'],
Event::ON_CLOSE => [Hyperf\WebSocketServer\Server::class, 'onClose'],
],
],
],
```
## 配置路由
> 目前暫時只支持配置文件的模式配置路由,後續會提供註解模式。
`config/routes.php` 文件內增加對應 `ws` 的 Server 的路由配置,這裏的 `ws` 值取決於您在 `config/autoload/server.php` 內配置的 WebSocket Server 的 `name` 值。
```php
<?php
Router::addServer('ws', function () {
Router::get('/', 'App\Controller\WebSocketController');
});
```
## 創建對應控制器
```php
<?php
declare(strict_types=1);
namespace App\Controller;
use Hyperf\Contract\OnCloseInterface;
use Hyperf\Contract\OnMessageInterface;
use Hyperf\Contract\OnOpenInterface;
use Swoole\Http\Request;
use Swoole\Server;
use Swoole\Websocket\Frame;
use Swoole\WebSocket\Server as WebSocketServer;
class WebSocketController implements OnMessageInterface, OnOpenInterface, OnCloseInterface
{
public function onMessage($server, Frame $frame): void
{
$server->push($frame->fd, 'Recv: ' . $frame->data);
}
public function onClose($server, int $fd, int $reactorId): void
{
var_dump('closed');
}
public function onOpen($server, Request $request): void
{
$server->push($request->fd, 'Opened');
}
}
```
接下來啟動 Server便能看到對應啟動了一個 WebSocket Server 並監聽於 9502 端口,此時您便可以通過各種 WebSocket Client 來進行連接和數據傳輸了。
```
$ php bin/hyperf.php start
[INFO] Worker#0 started.
[INFO] WebSocket Server listening at 0.0.0.0:9502
[INFO] HTTP Server listening at 0.0.0.0:9501
```
!> 當我們同時監聽了 HTTP Server 的 9501 端口和 WebSocket Server 的 9502 端口時, WebSocket Client 可以通過 9501 和 9502 兩個端口連接 WebSocket Server即連接 `ws://0.0.0.0:9501``ws://0.0.0.0:9502` 都可以成功。
因為 Swoole\WebSocket\Server 繼承自 Swoole\Http\Server可以使用 HTTP 觸發所有 WebSocket 的推送,瞭解詳情可查看 [Swoole 文檔](https://wiki.swoole.com/#/websocket_server?id=websocketserver) onRequest 回調部分。
如需關閉,可以修改 `config/autoload/server.php` 文件給 `http` 服務中增加 `open_websocket_protocol` 配置項。
```php
<?php
return [
// 這裏省略了該文件的其它配置
'servers' => [
[
'name' => 'http',
'type' => Server::SERVER_HTTP,
'host' => '0.0.0.0',
'port' => 9501,
'sock_type' => SWOOLE_SOCK_TCP,
'callbacks' => [
Event::ON_REQUEST => [Hyperf\HttpServer\Server::class, 'onRequest'],
],
'settings' => [
'open_websocket_protocol' => false,
]
],
]
];
```
## 連接上下文
WebSocket 服務的 onOpen, onMessage, onClose 回調並不在同一個協程下觸發因此不能直接使用協程上下文存儲狀態信息。WebSocket Server 組件提供了 **連接級** 的上下文API 與協程上下文完全一樣。
```php
<?php
declare(strict_types=1);
namespace App\Controller;
use Hyperf\Contract\OnMessageInterface;
use Hyperf\Contract\OnOpenInterface;
use Hyperf\WebSocketServer\Context;
use Swoole\Http\Request;
use Swoole\Websocket\Frame;
use Swoole\WebSocket\Server as WebSocketServer;
class WebSocketController implements OnMessageInterface, OnOpenInterface
{
public function onMessage($server, Frame $frame): void
{
$server->push($frame->fd, 'Username: ' . Context::get('username'));
}
public function onOpen($server, Request $request): void
{
Context::set('username', $request->cookie['username']);
}
}
```
## 多 server 配置
```
# /etc/nginx/conf.d/ng_socketio.conf
# 多個 ws server
upstream io_nodes {
server ws1:9502;
server ws2:9502;
}
server {
listen 9502;
# server_name your.socket.io;
location / {
proxy_set_header Upgrade "websocket";
proxy_set_header Connection "upgrade";
# proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
# proxy_set_header Host $host;
# proxy_http_version 1.1;
# 轉發到多個 ws server
proxy_pass http://io_nodes;
}
}
```
## 消息發送器
當我們想在 `HTTP` 服務中,關閉 `WebSocket` 連接時,可以直接使用 `Hyperf\WebSocketServer\Sender`
`Sender` 會判斷 `fd` 是否被當前 `Worker` 所持有,如果是,則會直接發送數據,如果不是,則會通過 `PipeMessage` 發送給除自己外的所有 `Worker`,然後由其他 `Worker` 進行判斷,
如果是自己持有的 `fd`,就會發送對應數據到客户端。
`Sender` 支持 `push``disconnect` 兩個 `API`,如下:
```php
<?php
declare(strict_types=1);
namespace App\Controller;
use Hyperf\Di\Annotation\Inject;
use Hyperf\HttpServer\Annotation\AutoController;
use Hyperf\WebSocketServer\Sender;
/**
* @AutoController
*/
class ServerController
{
/**
* @Inject
* @var Sender
*/
protected $sender;
public function close(int $fd)
{
go(function () use ($fd) {
sleep(1);
$this->sender->disconnect($fd);
});
return '';
}
public function send(int $fd)
{
$this->sender->push($fd, 'Hello World.');
return '';
}
}
```