tp5中使用tcp和websocket处理数据
安装workerman
composer require workerman/workerman
composer require workerman/workerman-for-win
由于是windows环境,所以要写两个文件才能起来两个worker服务
新建TCP使用的控制器文件
application/worker(自己需要的目录)/Worker.php
此文件将硬件的数据通过tcp接收进来,处理后转发给websocket和存储数据库
<?php
namespace app\worker\controller;
use GatewayClient\GatewayProtocol;
use think\worker\Server;
use GatewayClient\Gateway;
use Workerman\Connection\AsyncTcpConnection;
use Workerman\Lib\Timer;
use Workerman\Protocols\Ws;
date_default_timezone_set("PRC");
class Worker extends Server
{
protected $socket = 'tcp://127.0.0.1:24001';
protected $processes = 1;
public $ws_server;
protected static $heartbeat_time = 30;
/**
* 每个进程启动
* @param $worker
*/
public function onWorkerStart($worker)
{
echo "TCP服务启动了\n";
// 以websocket协议连接远程websocket服务器
$ws_connection = new AsyncTcpConnection("ws://127.0.0.1:21001");
// 每隔55秒向服务端发送一个opcode为0x9的websocket心跳(可选)
$ws_connection->websocketPingInterval = 30;
// 设置http头(可选)
$ws_connection->headers = [
'Cookie' => 'PHPSID=82u98fjhakfusuanfnahfi; token=2hf9a929jhfihaf9i',
'OtherKey' => 'values'
];
// 设置数据类型(可选)
$ws_connection->websocketType = Ws::BINARY_TYPE_BLOB; // BINARY_TYPE_BLOB为文本 BINARY_TYPE_ARRAYBUFFER为二进制
// 当TCP完成三次握手后(可选)
$ws_connection->onConnect = function($connection){
echo "websocket connected\n";
};
// 当websocket完成握手后(可选)
$ws_connection->onWebSocketConnect = function(AsyncTcpConnection $con, $response) {
echo $response;
$con->send('websocket连接成功');
};
// 远程websocket服务器发来消息时
$ws_connection->onMessage = function($connection, $data){
echo "来自websocket服务器的消息: $data\n";
};
// 连接上发生错误时,一般是连接远程websocket服务器失败错误(可选)
$ws_connection->onError = function($connection, $code, $msg){
echo "错误: $msg\n";
};
// 当连接远程websocket服务器的连接断开时(可选,建议加上重连)
$ws_connection->onClose = function($connection){
echo "连接已关闭并尝试重新连接\n";
// 如果连接断开,1秒后重连
$connection->reConnect(1);
};
// 设置好以上各种回调后,执行连接操作
$ws_connection->connect();
$this->ws_server = $ws_connection;
// //心跳检测---服务端不需要
// Timer::add(3, function()use($worker){
// $time_now = time();
// foreach ($worker->connections as $connection) {
// if (empty($connection->lastMessageTime)) {
// $connection->lastMessageTime = $time_now;
// }
// #判断心跳时间
// if ($time_now - $connection->lastMessageTime > self::$heartbeat_time) {
$connection->send(1);
$connection->close();
// }
$connection->send('heartbeat');
// }
//
// });
}
/**
* 收到信息
* @param $connection
* @param $data
*/
public function onMessage($connection, $data)
{
$connection->lastMessageTime = time();
#处理$data的逻辑业务
$ws_data = '数据';
#转发websocket数据
$ws_arr = [
'code' => 1,
'msg' => 'ok',
'time' => time(),
'data' => ['content' => $ws_data]
];
$this->ws_server->send(json_encode($ws_arr, 256) . "\r\n");
echo "转发websocket成功!";
#回复消息
$connection->lastMessageTime = time();
$connection->send(json_encode('获得成功'));
}
/**
* 当连接建立时触发的回调函数
* @param $connection
*/
public function onConnect($connection)
{
echo "连接 建立" . $connection->getRemoteIp() . ":". $connection->getRemotePort() ."\n";
$connection->send(json_encode('connect success'));
}
/**
* 当连接断开时触发的回调函数
* @param $connection
*/
public function onClose($connection)
{
echo "连接 关闭\n";
#断线1秒后重新连接
$connection->reConnect(1);
}
/**
* 当客户端的连接上发生错误时触发
* @param $connection
* @param $code
* @param $msg
*/
public function onError($connection, $code, $msg)
{
echo "错误: $code $msg\n";
}
public function onWorkerReload($worker)
{
foreach($worker->connections as $connection)
{
$connection->send('worker reloading');
}
}
}
新建Websocket使用的控制器文件
application/worker(自己需要的目录)/Websocket.php
此文件将websocket接收到到的数据发给客户端
<?php
namespace app\worker\controller;
use think\worker\Server;
date_default_timezone_set("PRC");
class Websecket extends Server
{
protected $socket = 'Websocket://127.0.0.1:21001';
protected $processes = 4;
protected static $heartbeat_time = 30;
public $userlist=[];
/**
* 收到信息
* @param $connection
* @param $data
*/
public function onMessage($connection, $data)
{
global $worker;
$fd = $connection->id;
echo '收到来自_id='.$fd.'_客户端的消息:'.$data."\r\n";
$connection->lastMessageTime = time();
if(!isset($connection->uid)){
$connection->uid=$connection->id; //此处为了方便测试把会话id作为用户id 详情见官方文档
$connection->worker->connections[$connection->uid] = $connection;
}
// 遍历当前进程所有的客户端连接
foreach($connection->worker->connections as $key=> $con)
{
//
if ($key != $fd){
// echo $con->id.'--------'.$connection->id;
// continue;
echo $con->id.'======'.$connection->id;
$con->send($data);
}
}
}
/**
* 当连接建立时触发的回调函数
* @param $connection
*/
public function onConnect($connection)
{
echo "websocket连接 建立:".$connection->getRemoteIp() . ":". $connection->getRemotePort()."{$connection->id}\n";
$connection->send(json_encode('websocket connect success'));
}
/**
* 当连接断开时触发的回调函数
* @param $connection
*/
public function onClose($connection)
{
echo "websocket连接 关闭\n";
$connection->reConnect(1);
}
/**
* 当客户端的连接上发生错误时触发
* @param $connection
* @param $code
* @param $msg
*/
public function onError($connection, $code, $msg)
{
echo "websocket错误: $code $msg\n";
}
/**
* 每个进程启动
* @param $worker
*/
public function onWorkerStart($worker)
{
echo "WebSocket服务启动了\n";
foreach($worker->connections as $connection)
{
$connection->send(time().'========websocket');
}
}
public function onWorkerReload($worker)
{
foreach($worker->connections as $connection)
{
$connection->send('websocket worker reloading');
}
}
}
在public下新建2个启动文件
public/worker_tcp.php
#!/usr/bin/env php
<?php
define('APP_PATH', __DIR__ . '/../application/');
define('BIND_MODULE','worker/Worker');
// 加载框架引导文件
require __DIR__ . '/../thinkphp/start.php';
public/worker_web.php
#!/usr/bin/env php
<?php
define('APP_PATH', __DIR__ . '/../application/');
define('BIND_MODULE','worker/Websecket');
// 加载框架引导文件
require __DIR__ . '/../thinkphp/start.php';
启动方法
在public目录下
启动tcp
php worker_tcp.php
启动websocket
php worker_web.php
不过写两个bat文件启动更方便:
php worker_tcp.php
php worker_web.php
完毕!!!