Laravel8使用laravel-s實現WebSocket服務器.通過 詳解基于Laravel8下的LaravelS實現高性能HTTP服務器 我們安裝了LaravelS并實現了一個HTTP服務器,那么本篇文章在此基礎上在實現一個Websocket服務器。
創建WebSocketService類
創建WebSocketService
類并實現Hhxsv5\LaravelS\Swoole\WebSocketHandlerInterface
類。
文件位置:app/Services/WebSocketService.php
<?php namespace App\Services; use Hhxsv5\LaravelS\Swoole\WebSocketHandlerInterface; use Swoole\Http\Request; use Swoole\Http\Response; use Swoole\WebSocket\Frame; use Swoole\WebSocket\Server; use Illuminate\Support\Facades\Log; /** * @see https://wiki.swoole.com/#/start/start_ws_server */ class WebSocketService implements WebSocketHandlerInterface { // 聲明沒有參數的構造函數 public function __construct() { } public function onOpen(Server $server, Request $request) { // 在觸發onOpen事件之前,建立WebSocket的HTTP請求已經經過了Laravel的路由, // 所以Laravel的Request、Auth等信息是可讀的,Session是可讀寫的,但僅限在onOpen事件中。 // \Log::info('New WebSocket connection', [$request->fd, request()->all(), session()->getId(), session('xxx'), session(['yyy' => time()])]); // 此處拋出的異常會被上層捕獲并記錄到Swoole日志,開發者需要手動try/catch Log::info('WebSocket 建立連接'); $server->push($request->fd, 'Welcome to LaravelS'); } public function onMessage(Server $server, Frame $frame) { // \Log::info('Received message', [$frame->fd, $frame->data, $frame->opcode, $frame->finish]); // 此處拋出的異常會被上層捕獲并記錄到Swoole日志,開發者需要手動try/catch $server->push($frame->fd, date('Y-m-d H:i:s')); } public function onClose(Server $server, $fd, $reactorId) { Log::info('websocket 關閉'); } }
修改配置文件
文件位置:app/config/laravels.php
'websocket' => [ 'enable' => true, // 注意:設置enable為true 'handler' => \App\Services\WebSocketService::class, ], 'swoole' => [ //... // dispatch_mode只能設置為2、4、5,https://wiki.swoole.com/#/server/setting?id=dispatch_mode 'dispatch_mode' => 2, //... ],
修改nginx配置文件
文件位置:/usr/local/nginx/conf/vhosts/laravel_s.conf
map $http_upgrade $connection_upgrade { default upgrade; '' close; } upstream laravels { # 此處注意,我使用的是Linux系統環境,因此這里使用的是Linux中的IP地址 # 由于默認是127.0.0.1,或是在Linux中可能會導致websocket無法完成服務 server 192.168.133.131:5200 weight=5 max_fails=3 fail_timeout=30s; keepalive 16; } server { listen 80; server_name laravel-s.com; root /www/laravel_swoole/public; index index.php index.html index.htm; location / { try_files $uri @laravels; } # 配置websockeet location = /ws { proxy_http_version 1.1; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Real-PORT $remote_port; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header Host $http_host; proxy_set_header Scheme $scheme; proxy_set_header Server-Protocol $server_protocol; proxy_set_header Server-Name $server_name; proxy_set_header Server-Addr $server_addr; proxy_set_header Server-Port $server_port; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection $connection_upgrade; proxy_pass http://laravels; } # 配置laravels location @laravels { proxy_http_version 1.1; proxy_set_header Connection ""; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Real-PORT $remote_port; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header Host $http_host; proxy_set_header Scheme $scheme; proxy_set_header Server-Protocol $server_protocol; proxy_set_header Server-Name $server_name; proxy_set_header Server-Addr $server_addr; proxy_set_header Server-Port $server_port; proxy_pass http://laravels; } }
注意:laravels配置中的server需要填寫本機IP。由于我使用的是Linux系統,因此配置了Linux系統中的IP。
配置.env文件
LARAVELS_LISTEN_IP=192.168.133.131 LARAVELS_DAEMONIZE=true
LARAVELS_LISTEN_IP要與upstream laravels配置的IP一樣
啟動laravels
切換到應用根目錄,啟動laravels
php bin/laravels start
重啟nginx服務器
systemctl restart nginx.service
編寫前端界面
testSwoole.html,此界面位于windows
<!DOCTYPE html> <html> <head> <title></title> </head> <body> <input id="input" style="width: 100%;" > <script> window.onload = function() { let nick = prompt('Enter your nickname'); let input = document.getElementById('input'); input.focus(); // 初始化客戶端套接字并建立連接 let socket = new WebSocket("ws://laravel-s.com/ws"); // 建立連接時觸發 socket.onopen = function(event) { console.log('連接開始...'); } // 接收到服務器信息 socket.onmessage = function(event) { let msg = event.data; let node = document.createTextNode(msg); let div = document.createElement('div'); div.appendChild(node); document.body.insertBefore(div,input); input.scrollIntoView(); } // 關閉連接時觸發 socket.onclose = function(event) { console.log('連接關閉...'); } input.onchange = function() { let msg = nick + ": " + input.value; socket.send(msg); input.value=""; } } </script> </body> </html>
尤其注意 let socket = new WebSocket("ws://laravel-s.com/ws"); 此時已經變成域名,默認80端口。當訪問此頁面并輸入信息后,客戶端發起協議升級請求后將信息發送到指定地址
打開該html頁面并輸入信息后查看websocket通信請求過程,可以看到協議已經升級為websocket
# 通用頭 General Request URL: ws://laravel-s.com/ws Request Method: GET Status Code: 101 Switching Protocols # 響應頭 Connection: upgrade Date: Wed, 10 Mar 2021 16:39:21 GMT Sec-Websocket-Version: 13 Upgrade: websocket # 通用頭 Sec-WebSocket-Extensions: permessage-deflate; client_max_window_bits Sec-WebSocket-Key: LeAIasPj2v1aK4TKw0QqsA== Sec-WebSocket-Version: 13 Upgrade: websocket
關于報錯
剛編寫完成并運行時,websocket客戶端(也就是瀏覽器運行的testSwoole.html)出現failed: Error during WebSocket handshake: Unexpected response code: 404
錯誤,這個錯誤如何解決?都知道404為頁面錯誤,根據這個我仔細檢查了代碼,沒有錯誤,下面來說說我的解決方法吧, 雖然有些不知所以然。
解決方法
1)重啟 laravels
2)重啟nginx
我就是這樣解決了這個錯誤,如何你也遇到同樣的問題,歡迎一起交流學習。