1 安装swoole插件
我php的版本是7.3, Swoole4.
2 安装think-swoole
在站点根目录下运行
composer require topthink/think-swoole:2.0.0
3 修改站点ngnix配置文件
添加wss节点
location /wss {
proxy_pass http://127.0.0.1:9501;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
rewrite /wss/(.*) /$1 break;
proxy_redirect off;
proxy_connect_timeout 5s; #配置点1
proxy_read_timeout 60000s; #配置点2,如果没效,可以考虑这个时间配置长一点
proxy_send_timeout 60000s; #配置点3
}
4 服务端代码
<?php
namespace app\common\library;
use fast\Random;
use think\Hook;
use think\swoole\Server;
/**
* 短信验证码类
*/
class WSServer extends Server
{
protected $host = '127.0.0.1'; //监听所有地址
protected $port = 9501; //监听9501端口
protected $serverType = 'socket';
protected $option = [
'worker_num'=> 4, //设置启动的Worker进程数
'daemonize' => false, //守护进程化(上线改为true)
'backlog' => 128, //Listen队列长度
'dispatch_mode' => 2, //固定模式,保证同一个连接发来的数据只会被同一个worker处理
//心跳检测:每60秒遍历所有连接,强制关闭10分钟内没有向服务器发送任何数据的连接
'heartbeat_check_interval' => 60,
'heartbeat_idle_time' => 600
];
//建立连接时回调函数
public function onOpen($server, $req)
{
$fd = $req->fd;//客户端标识
$uid = $req->get['uid'];//客户端传递的用户id
$token = $req->get['token'];//客户端传递的用户登录token
//省略token验证逻辑......
if (!$token) {
$arr = array('status'=> 2,'message'=> 'token已过期');
$server->push($fd, json_encode($arr));
$server->close($fd);
return;
}
//省略给用户绑定fd逻辑......
echo "用户{$uid}建立了连接,标识为{$fd}\n";
}
//接收数据时回调函数
public function onMessage($server, $frame)
{
$fd = $frame->fd;
$message = $frame->data;
//省略通过fd查询用户uid逻辑......
$uid = 666;
$data['uid'] = $uid;
$data['message'] = '用户'.$uid.'发送了:'.$message;
$data['post_time'] = date("m/d H:i",time());
$arr = array('status'=> 1,'message'=> 'success','data'=> $data);
//仅推送给当前连接用户
//$server- push($fd, json_encode($arr));
//推送给全部连接用户
foreach($server->connections as $fd) {
$server->push($fd, json_encode($arr));
}
}
//连接关闭时回调函数
public function onClose($server, $fd)
{
echo "标识{$fd}关闭了连接\n";
}
}
5 启动服务端代码
$ws = new WSServer();
$ws->start();
6 前端代码
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
</head>
<body>
<div id="mainbody">
<div class="container">
<div class="text-center">
<div>status:</div>
<div id="status"></div>
<button id="connectButton">连接服务器</button>
<button id="disconnectButton">断开</button>
<input id="messageInput" type="text">
<button id="sendButton">发送信息</button>
<div>收到服务端信息</div>
<div id="messages">messages: </div>
</div>
</div>
</div>
<script>
$(document).ready(function () {
var socket;
// 打开WebSocket连接
function openSocket() {
socket = new WebSocket("wss://${你的域名}/wss?uid=1001&token=20001");
// 连接成功时的处理
socket.onopen = function (event) {
$("#status").text("连接状态:已连接");
login();
};
// 接收到消息时的处理
socket.onmessage = function (event) {
var message = event.data;
$("#messages").append("<p>" + message + "</p>");
};
// 连接关闭时的处理
socket.onclose = function (event) {
$("#status").text("连接状态:已关闭");
};
}
// 关闭WebSocket连接
function closeSocket() {
socket.close();
}
function login() {
var message = { 'uid': 1001, 'token': '12jja' };
socket.send(JSON.stringify(message));
}
// 发送消息到WebSocket服务器
function sendMessage() {
var message = $("#messageInput").val();
socket.send(message);
$("#messageInput").val("");
}
// 处理连接按钮的点击事件
$("#connectButton").click(function () {
openSocket();
});
// 处理断开连接按钮的点击事件
$("#disconnectButton").click(function () {
closeSocket();
});
// 处理发送消息按钮的点击事件
$("#sendButton").click(function () {
sendMessage();
});
});
</script>
</body>
</html>
说明:经过nginx反向代理设置后,客户端连接服务端不需要在带上端口,解决了同源跨域限制问题。