目录
2.thinkphp框架安装GatewayClient ,在tp项目框架composer.json目录(项目根目录)使用composer运行安装。
1.GatewayWorker的demo
- workerman官网下载demo: gateway-worker 手册
- 1.2 下载绑定资源
- 1.3 在我的上传资源中去下载
2.thinkphp框架安装GatewayClient ,在tp项目框架composer.json目录(项目根目录)使用composer运行安装。
composer require workerman/gatewayclient
3. 配置和启动GatewayWorker
3.1 配置
GatewayWorker/ApplicationsYourApp/start_register.php文件配置内容如下
<?php
/**
* This file is part of workerman.
*
* Licensed under The MIT License
* For full copyright and license information, please see the MIT-LICENSE.txt
* Redistributions of files must retain the above copyright notice.
*
* @author walkor<walkor@workerman.net>
* @copyright walkor<walkor@workerman.net>
* @link http://www.workerman.net/
* @license http://www.opensource.org/licenses/mit-license.php MIT License
*/
use \Workerman\Worker;
use \GatewayWorker\Register;
// 自动加载类
require_once __DIR__ . '/../../vendor/autoload.php';
// register 必须是text协议,切记不能将register端口开放给外网
$register = new Register('text://127.0.0.1:1238');
// 如果不是在根目录启动,则运行runAll方法
if(!defined('GLOBAL_START'))
{
Worker::runAll();
}
GatewayWorker/ApplicationsYourApp/start_gateway.php 配置文件内容如下
<?php
/**
* This file is part of workerman.
*
* Licensed under The MIT License
* For full copyright and license information, please see the MIT-LICENSE.txt
* Redistributions of files must retain the above copyright notice.
*
* @author walkor<walkor@workerman.net>
* @copyright walkor<walkor@workerman.net>
* @link http://www.workerman.net/
* @license http://www.opensource.org/licenses/mit-license.php MIT License
*/
use \Workerman\Worker;
use \Workerman\WebServer;
use \GatewayWorker\Gateway;
use \GatewayWorker\BusinessWorker;
use \Workerman\Autoloader;
// 自动加载类
require_once __DIR__ . '/../../vendor/autoload.php';
// gateway 进程,这里使用Text协议,可以用telnet测试
$gateway = new Gateway("websocket://0.0.0.0:8282");
// gateway名称,status方便查看
$gateway->name = 'YourAppGateway';
// gateway进程数,一般设置2个就足够
$gateway->count = 2;
// 本机ip,分布式部署时使用内网ip
$gateway->lanIp = '127.0.0.1';
// 内部通讯起始端口,假如$gateway->count=2,起始端口为2900
// 则一般会使用2900 2901 2个端口作为内部通讯端口
$gateway->startPort = 2900;
// 服务注册地址
$gateway->registerAddress = '127.0.0.1:1238';
// 心跳间隔
//$gateway->pingInterval = 10;
// 心跳数据
//$gateway->pingData = '{"type":"ping"}';
/*
// 当客户端连接上来时,设置连接的onWebSocketConnect,即在websocket握手时的回调
$gateway->onConnect = function($connection)
{
$connection->onWebSocketConnect = function($connection , $http_header)
{
// 可以在这里判断连接来源是否合法,不合法就关掉连接
// $_SERVER['HTTP_ORIGIN']标识来自哪个站点的页面发起的websocket链接
if($_SERVER['HTTP_ORIGIN'] != 'http://kedou.workerman.net')
{
$connection->close();
}
// onWebSocketConnect 里面$_GET $_SERVER是可用的
// var_dump($_GET, $_SERVER);
};
};
*/
// 如果不是在根目录启动,则运行runAll方法
if(!defined('GLOBAL_START'))
{
Worker::runAll();
}
GatewayWorker/ApplicationsYourApp/start_businessworker.php配置文件内容如下
<?php
/**
* This file is part of workerman.
*
* Licensed under The MIT License
* For full copyright and license information, please see the MIT-LICENSE.txt
* Redistributions of files must retain the above copyright notice.
*
* @author walkor<walkor@workerman.net>
* @copyright walkor<walkor@workerman.net>
* @link http://www.workerman.net/
* @license http://www.opensource.org/licenses/mit-license.php MIT License
*/
use \Workerman\Worker;
use \Workerman\WebServer;
use \GatewayWorker\Gateway;
use \GatewayWorker\BusinessWorker;
use \Workerman\Autoloader;
// 自动加载类
require_once __DIR__ . '/../../vendor/autoload.php';
// bussinessWorker 进程
$worker = new BusinessWorker();
// worker名称
$worker->name = 'YourAppBusinessWorker';
// bussinessWorker进程数量
$worker->count = 4;
// 服务注册地址
$worker->registerAddress = '127.0.0.1:1238';
// 如果不是在根目录启动,则运行runAll方法
if(!defined('GLOBAL_START'))
{
Worker::runAll();
}
GatewayWorker/ApplicationsYourApp/Events.php内容如下
<?php
/**
* This file is part of workerman.
*
* Licensed under The MIT License
* For full copyright and license information, please see the MIT-LICENSE.txt
* Redistributions of files must retain the above copyright notice.
*
* @author walkor<walkor@workerman.net>
* @copyright walkor<walkor@workerman.net>
* @link http://www.workerman.net/
* @license http://www.opensource.org/licenses/mit-license.php MIT License
*/
/**
* 用于检测业务代码死循环或者长时间阻塞等问题
* 如果发现业务卡死,可以将下面declare打开(去掉//注释),并执行php start.php reload
* 然后观察一段时间workerman.log看是否有process_timeout异常
*/
//declare(ticks=1);
use \GatewayWorker\Lib\Gateway;
/**
* 主逻辑
* 主要是处理 onConnect onMessage onClose 三个方法
* onConnect 和 onClose 如果不需要可以不用实现并删除
*/
class Events
{
/**
* 当客户端连接时触发
* 如果业务不需此回调可以删除onConnect
*
* @param int $client_id 连接id
*/
public static function onConnect($client_id)
{
Gateway::setSession($client_id, array('name'=>"张三".rand(0,9), 'sex'=>"男".rand(0,9)));
$_SESSION =
//推送给当前client_id
Gateway::sendToClient($client_id, json_encode(array(
'type' => 'init',
'client_id' => $client_id
)));
//推送给所有人
Gateway::sendToAll(json_encode([
'type' => 'login',
'client_id' => $client_id
]));
//tcp协议
// 向当前client_id发送数据
// Gateway::sendToClient($client_id, "Hello $client_id\r\n");
// // 向所有人发送
// Gateway::sendToAll("$client_id login\r\n");
}
/**
* 当客户端发来消息时触发
* @param int $client_id 连接id
* @param mixed $message 具体消息
*/
public static function onMessage($client_id, $message)
{
// 向所有人发送
Gateway::sendToAll("$client_id said $message\r\n");
}
/**
* 当用户断开连接时触发
* @param int $client_id 连接id
*/
public static function onClose($client_id)
{
//websocket协议
//推送给所有人
Gateway::sendToAll(json_encode([
'type' => 'logout',
'client_id' => $client_id
]));
// // 向所有人发送
// GateWay::sendToAll("$client_id logout\r\n");
}
}
3.2 按照下图启动GatewayWorker服务
下图为window系统启动成功界面
4. 在tp6框架中去使用GatewayClient
创建一个控制器,引入GatewayClient类进行使用就可以了。
<?php
namespace app\index\controller;
// GatewayClient 3.0.0版本开始要使用命名空间
use app\api\controller\BaseApiController;
use GatewayClient\Gateway;
class ImController extends BaseApiController
{
public function login(){
$client_id = request()->param('client_id');
if(empty($client_id)){
return $this->fail('client_id不能为空。');
}
Gateway::setSession($client_id, ['uid'=>rand(888,999),'name'=>"张三".rand(0,9), 'sex'=>"男".rand(0,9)]);
return $this->success("登录成功",['uid'=>rand(888,999),'name'=>"张三".rand(0,9), 'sex'=>"男".rand(0,9)]);
}
/**
* @return \think\response\Json
* @notes 当前在线人数
* @author ssx
*/
public function getAllClientIdCount(){
$count = Gateway::getAllClientIdCount();
return $this->success("当前在线{$count}人");
}
/**
* @return \think\response\Json
* @notes 获取当前所有在线client_id信息
* @author ssx
*/
public function getAllClientSessions(){
$list = Gateway::getAllClientSessions();
return $this->success("当前所有在线client_id信息",$list);
}
/**
* @return \think\response\Json
* @notes 将client_id加入某个组,以便通过Gateway::sendToGroup发送数据
* @author ssx
*/
public function joinGroup(){
$client_id = request()->param('client_id');
if(empty($client_id)){
return $this->fail('client_id不能为空。');
}
// 加入某个群组(可调用多次加入多个群组)
// 加入某个群组(可调用多次加入多个群组)
$group = [1,2,3,4,5];
$group = array_rand($group);
Gateway::joinGroup($client_id, $group);
return $this->success("加入组成功,组id:".$group,Gateway::getClientSessionsByGroup($group));
}
/**
* @return \think\response\Json
* @notes 获取全局所有在线组及成员
* @author ssx
*/
public function getAllGroupInfo(){
$list = Gateway::getAllGroupIdList();
foreach ($list as $key => &$value) {
//根据组id获取该组下所有client_id
$client_id = Gateway::getClientIdListByGroup($value);
$list[$key] = array_values($client_id);
}
$str = json_encode($list);
return $this->success("当前在线组:{$str}",$list);
}
/**
* @return \think\response\Json|void
* @notes 给指定client_id发送消息
* @author ssx
*/
public function send(){
$group = [1,2,3,4,5];
var_dump(array_rand($group));die;
$client_id = request()->param('client_id');
if(empty($client_id)){
return $this->fail('client_id不能为空。');
}
$message = "你好aaaHello".$client_id;
// 如果不在线就先存起来
if(!Gateway::isOnline($client_id))
{
// 保存未读消息
echo '用户{$client_id}不在线';
} else {
// 在线就转发消息给对应的客户端
Gateway::sendToClient($client_id,json_encode([
'type'=>'message',
'msg'=>$message,
]));
}
return $this->success("向{$client_id}发送消息:{$message}。");
}
}
5.前端简单demo
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>ceshi</title>
</head>
<body>
<p id="num" style="color:rosybrown"></p>
<p id="num_info" style="color:rosybrown"></p>
<p id="group" style="color:royalblue"></p>
<div id="aaa"></div>
</body>
</html>
<script !src="">
// 获取id为'aaa'的元素
var content = document.getElementById('aaa');
var num = document.getElementById('num');
var num_info = document.getElementById('num_info');
var group = document.getElementById('group');
/**
* 与GatewayWorker建立websocket连接,域名和端口改为你实际的域名端口,
* 其中端口为Gateway端口,即start_gateway.php指定的端口。
* start_gateway.php 中需要指定websocket协议,像这样
* $gateway = new Gateway(websocket://0.0.0.0:7272);
*/
ws = new WebSocket("ws://127.0.0.1:8282");
// 服务端主动推送消息时会触发这里的onmessage
ws.onmessage = function(e){
console.log(e)
// 假设WebSocket服务器使用了UTF-8编码
console.log(e.data)
// json数据转换成js对象
var data = eval("("+e.data+")");
console.log(data)
var type = data.type || '';
//登录
fetchData('http://im.api/index/im/login?client_id='+ data.client_id, function (data) {});
//查询在线人数
fetchData('http://im.api/index/im/getALLClientIdCount', function (data) {
num.innerHTML = data.msg;
});
//获取当前所有在线client_id信息
fetchData('http://im.api/index/im/getAllClientSessions', function (data) {
num_info.innerHTML = "在线client_id的信息:" + JSON.stringify(data.data);
});
//查询在线组及其组下成员
fetchData('http://im.api/index/im/getAllGroupInfo', function (data) {
group.innerHTML = data.msg;
});
switch(type){
// Events.php中返回的init类型的消息,将client_id发给后台进行uid绑定
case 'init':
// 使用innerHTML设置HTML内容
content.innerHTML += "登录成功,你的client_id:" + data.client_id + "<br>";
//加入一个群组
fetchData('http://im.api/index/im/joinGroup?client_id='+ data.client_id , function (data) {
group.innerHTML = data.msg;
});
break;
case 'login':
// 使用innerHTML设置HTML内容
content.innerHTML += "用户登录,client_id:" + data.client_id + "<br>";
break;
case 'logout':
// 使用innerHTML设置HTML内容
content.innerHTML += "用户下线,client_id:" + data.client_id + "<br>";
break;
default :
// 使用innerHTML设置HTML内容
content.innerHTML += "收到推送消息:" + data.msg + "<br>";
}
};
function fetchData(url,callback) {
var xhr = new XMLHttpRequest();
xhr.onreadystatechange = function () {
if (xhr.readyState == 4 && xhr.status == 200) {
//json转对象
var data = JSON.parse(xhr.responseText);
console.log(data);
callback(data);
}
};
xhr.open('GET', url, true);
xhr.send();
}
</script>
效果实现如下图