1.准备工作
1.gateway-worker扩展 (composer仓库中的插件与文档存在一定偏差,所以采用提供的demo包)gateway-worker
2.将下载的文件放在extend目录下(此demo的gateway-worker是3.1.0)
3.windows环境下启动extend文件夹之下的start_for_windows.bat
4.安装gateway-client扩展 –GatewayWorker3.0.8及以上版本请使用 3.0.13版本的GatewayClient
composer require workerman/gatewayclient -v 3.0.13
5.extend\GatewayWorker\vendor\workerman\gateway-worker\src\Lib 该目录下有gateway.php 修改配置
6.注意!!! 业务逻辑都是gatewayclient完成,gatewayworker只做一个启动服务与配置的操作,同时需要注意gateway_client的版本和gatewayworker是否适配
2.代码内容
events内容
class Events
{
/**
* 当客户端连接时触发
* 如果业务不需此回调可以删除onConnect
*
* @param int $client_id 连接id
*/
public static function onConnect($client_id)
{
$resData = [
'type' => 'init',
'client_id' => $client_id,
'msg' => 'connect is success' // 连接成功
];
Gateway::sendToClient($client_id, json_encode($resData));
}
/**
* 当客户端发来消息时触发
* @param int $client_id 连接id
* @param mixed $message 具体消息
*/
public static function onMessage($client_id, $message)
{
//message里需要包含user_id/order_id/client_id,业务参数自己定义
$msg = json_decode($message,true);
if (!$msg){
return '';
}
//可能存在一种情况,前端已经连接上了,但是用户一直刷新这个这个页面
$res = Gateway::getClientIdByUid($msg['user_id']);
var_dump($res);
//初始化
if ($msg['type'] == 'init'){
$returnMessage = [
'type' => 'init',
'content' => 'init',
];
if ($res){//如果绑定过就自动解绑,但是实际测试不解除绑定根据client_id获取到的只有一个数据并不是多个
Gateway::unbindUid($res[0],$msg['user_id']);
}
Gateway::bindUid($client_id,$msg['user_id']);
}
//如果是心跳检测
if ($msg['type'] == 'heartbeat'){
$returnMessage = [
'type' => 'heartbeat',
'content' => 'heartbeat',
];
}
//如果是正常数据
if ($msg['type'] == 'MessageReceived'){
$returnMessage = [
'type' => 'MessageReceived',
'content' => 'MessageReceived',
];
}
$returnMessage = json_encode($returnMessage);
Gateway::sendToClient($client_id,$returnMessage);
}
/**
* 当用户断开连接时触发
* @param int $client_id 连接id
*/
public static function onClose($client_id)
{
$resDatas = [
'type' => 'colse',
'client_id' => $client_id,
'msg' => 'close is success'
];
// 向所有人发送
GateWay::sendToAll(json_encode($resDatas));
}
}
重新连接之后client_id并不是多个 如图

模拟前端代码内容
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
这里只做接收消息展示用, 发送心跳包和init. 刷新页面可以看到init
init是链接成功展示,发送消息,在event里面可以看到
</body>
<script>
ws = new WebSocket("ws://"+document.domain+":8282");
ws.onopen = function() {
console.log("连接成功");
var init = JSON.stringify({"type":"init","user_id":"1"});
ws.send(init);
//10秒发送一次心跳包
var heart = JSON.stringify({"type":"heartbeat","user_id":"1"});
setInterval(function(){
ws.send(heart);
},10000);
};
// 服务端主动推送消息时会触发这里的onmessage
ws.onmessage = function(e){
console.log(e);
var data = eval("("+e.data+")");
// console.log(data);
var type = data.type || '';
// console.log(type);
switch(type){
case 'heartbeat':
console.log('heartbeat');
break;
// 当mvc框架调用GatewayClient发消息时直接打印出来
case 'MessageReceived':
// 接收到tp中主动发送数据提示的标记
console.log('MessageReceived', data);
break;
default :
console.log(e.data);
}
};
</script>
</html>
也可以这样连接
f12–控制台–输入 –new WebSocket(‘ws://localhost:8282’)
网络-WS-消息-可以看到client_id
连接成功如图所示

实际代码中调用
class Message extends Controller
{
// 发送消息 进行操作
public function send($serverId)
{
require_once ROOT_PATH . "extend/GatewayWorker/vendor/workerman/gateway-worker/src/Protocols/GatewayProtocol.php";
require_once ROOT_PATH . "extend/GatewayWorker/vendor/workerman/gateway-worker/src/Lib/Gateway.php";
require_once ROOT_PATH . "extend/GatewayWorker/vendor/workerman/gateway-worker/src/Lib/Context.php";
require_once ROOT_PATH . "extend/GatewayWorker/vendor/workerman/gateway-worker/src/Lib/Db.php";
require_once ROOT_PATH . "extend/GatewayWorker/vendor/workerman/gateway-worker/src/Lib/DbConnection.php";
$client_id = \GatewayWorker\Lib\Gateway::getClientIdByUid($serverId);
if ($client_id) {
//$num = count($client_id);
$resData = [
'type' => 'new_order',
'client_id' => $client_id,
// 'msg' => cdnurl(config('site.order_sound'), true),
'msg' => 'try this',
];
\GatewayWorker\Lib\Gateway::sendToClient($client_id[0], json_encode($resData));
}
}
}
}
重连机制
1.创建一个全局生命周期的定时器(注意只能创建一次,无限制创建定时器客户端会崩)
2.连接建立后,周期性的每30s向服务器发送心跳包,比如ping
3.服务器收到客户端心跳包ping时,回复pong
4.客户端仅记录最后收到pong的时间戳
4.由全局定时器检查网络状况是否良好
if (lastPong + 30 *1.5 < now) {
//超时重连 todo
1.连接时候用设备id和客户端id,请求接口绑定;
2.做好心跳
3.做好重连机制(低段位的肯定在onClose注册个断线重连,这种基本就是傻)
业务代码案例
//注册
\GatewayClient\Gateway::$registerAddress = '127.0.0.1:1238';
//是否在线
$res =\GatewayClient\Gateway::isOnline($info->client_id);
if ($res == 1){
\GatewayClient\Gateway::bindUid($info->client_id,$this->auth->id);
$socket = new Common();
$data = ['userinfo' => $this->auth->getUser()];
$data['userinfo']['token'] = $this->auth->getToken();
$socket->send($this->auth->id, 'scan_login',$data);
}
