首先从单服务器实现开始
我的系统是centos系统,lnmp搭建的环境,php5.6
1:首先检查环境是否支持
curl -Ss http://www.workerman.net/check.php | php
PHP Version >= 5.3.3 [OK]
Extension pcntl check [OK]
Extension posix check [OK]
2.通过composer安装workerman
或者也可以去我的网盘下载示例:
链接:https://pan.baidu.com/s/1LJccnNQlBVMj9SJcyWyFyw
提取码:v5h5
3.需要将Workerman中php文件,除了Lib/Constants.php外,其它所有的php文件添加上.class
否则无法引入,
将更改好的文件放入项目根目录的中的Application文件夹下
4.这个时候在我们自己的控制器目录里边创建一个WorkerManController.class.php文件
<?php
namespace WeChat\Controller;
use Common\Controller\EtomatoCMS;
use Workerman\Worker;
/**
* webSocket功能
* 通过服务器主动向客户端push内容
*/
class WorkerManController extends EtomatoCMS {
/**
* 构造函数
* @access public
*/
public function __construct(){
global $worker;
$worker = new \Workerman\Worker('websocket://0.0.0.0:8000'); //实例化 Websocket服务
$worker->count = 1; //设置进程数
// worker进程启动后建立一个内部通讯端口
$worker->onWorkerStart = function ($worker) {
// 开启一个内部端口,方便内部系统推送数据,Text协议格式 文本+换行符
$inner_text_worker = new Worker('Text://0.0.0.0:8001'); //8001
$inner_text_worker->onMessage = function ($connection, $buffer) {
// $data数组格式,里面有uid,表示向那个uid的页面推送数据
$data = json_decode($buffer,true);
$uid = $data['uid'];
// 发送数据
if ($uid) {
$res = $this->sendMessageByUid($uid, $data['info']);
} else {
$res = $this->broadcast($data['info']);
}
// 返回推送结果
$connection->send($res ? 'ok' : 'fail');
echo "inner text Msg $uid \n";
};
$inner_text_worker->listen();
echo "Start \n";
};
$worker->uidConnections = array();
// 当有客户端发来消息时执行的回调函数
$worker->onMessage = function ($connection, $data) use ($worker) {
// 判断当前客户端是否已经验证,既是否设置了uid
if (!isset($connection->uid)) {
// 没验证的话把第一个包当做uid(这里为了方便演示,没做真正的验证)
$connection->uid = $data;
/**
* 保存uid到connection的映射,这样可以方便的通过uid查找connection,
* 实现针对特定uid推送数据
*/
$worker->uidConnections[$connection->uid][$connection->id] = $connection;
}
echo $connection->uid."-".$connection->id."msg \n";
};
//当客户端连接时
$worker->onConnect = function ($connection){
echo $connection->id."connect \n";
};
// 当有客户端连接断开时
$worker->onClose = function ($connection) use ($worker) {
global $worker;
if (isset($connection->uid)) {
// 连接断开时删除映射
unset($worker->uidConnections[$connection->uid][$connection->id]);
}
echo $connection->uid."-".$connection->id."close \n";
};
// Run worker
Worker::runAll();
}
// 向所有验证的用户推送数据
public function broadcast($message)
{
global $worker;
foreach ($worker->uidConnections as $connection) {
$connection->send($message);
}
return true;
}
// 针对uid推送数据
public function sendMessageByUid($uid, $message){
global $worker;
if (isset($worker->uidConnections[$uid])) {
foreach($worker->uidConnections[$uid] as $connection) {
$connection->send($message);
}
return true;
}
return false;
}
}
5.此文件只能使用命令来启动,启动的方式为 进入项目的根目录,运行语句:
php index.php WeChat/WorkerMan start :此方式运行为dubug模式,方便联调测试
php index.php WeChat/WorkerMan start -d :此方式运行为守护进程模式,以便于关闭XShell工具后依旧能守住此进程,线上可稳定运行。
6.如果期间在运行过程中出现stream_socket_server 报错
失败原因:
stream_socket_server 函数被php.ini禁用
解决方法
1、运行php --ini 找到php.ini文件
2、打开php.ini找到disable_functions一项,将stream_socket_server禁用项删掉
重启php.ini命令 /usr/local/php/sbin/php-fpm reload
7.如何通过客户端的HTTP请求来实现workerman的相应,往设备端推送数据呢?
—这是我们公司物联网相关的数字标牌需要实现的,管理员配置好数据后,设备端需要主动接收到数据更改做出响应。。
/**
* 屏保功能,websocket建立连接。
* 当屏保数据发生更改之后,需要向客户端推送新的数据源
* */
public function actionSocket($id){
$objMsg = $this->callBackMsg($id, 1);
$client = stream_socket_client('tcp://127.0.0.1:8001', $errno, $errmsg, 1);
// 推送的数据,包含uid字段,表示是给这个uid推送
$data = array(
'uid' => 'uid'.$id,
'info' => json_encode($objMsg)
);
// 发送数据,注意8001端口是Text协议的端口,Text协议需要在数据末尾加上换行符
fwrite($client, json_encode($data) . "\n");
// 读取推送结果
$res = trim(fread($client, 8192),"\n");
$this->json_str(0, '', $res);
}
此处重点是使用了stream_socket_client(‘tcp://127.0.0.1:8001’, $errno, $errmsg, 1);
通过这个向内部推送消息,接收到消息,将消息发送给对应的uid用户
8.客户端需要做的事:
我这边只拿小程序来举例
uni.connectSocket({
url: 'wss://xxx.xxx.xxx/ws:8000' /*ssl证书下的域名方式*
// url:'ws://xxx.xxx.xxx/ws:8000' *没有ssl证书的方式 (服务器IP或者域名)*
});
建立连接后就可接收到服务器端主动推送过来的数据响应了。
此处重点想说的是websocket如何配置ssl证书
nginx.conf:
listen 443 ssl;
ssl_certificate /usr/local/nginx/conf/netitvtest.cn_bundle.crt;
ssl_certificate_key /usr/local/nginx/conf/netitvtest.cn.key;
ssl_protocols TLSv1.1 TLSv1.2; # Dropping SSLv3, ref: POODLE
ssl_prefer_server_ciphers on;
ssl_session_timeout 5m;
ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE:ECDH:AES:HIGH:!NULL:!aNULL:!MD5:!ADH:!RC4;
location /ws {
proxy_set_header X-Real-IP $remote_addr;
proxy_pass http://localhost:8000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "Upgrade";
proxy_set_header Remote_addr $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
重启nginx:/bin/systemctl restart nginx.server
单服务器实现workerman就此完成