您的位置 首页 php

Swoole: TcpServer+SocketServer+EMQTT组合,实现基础设备控制

大家好,我是物联好朋友小搜。

今天,我们结合前面的文章内容,进行一个简单的物联应用。

在物联网中最简单直接的应用就远程开关电灯,今天小搜将为大家一一讲述。

智能灯控

本文用到的技术和工具都相对简单,在阅读本文时,建议没有关注小搜往期文章的朋友回头去看看,有前面的一些知识有所了解。

回顾完全文后,正式进入主题。

一、设计一个简单实用的架构

系统架构

从上图可以看出,云端需要建立三个服务,分别是:tcp服务、mqtt服务、websocket服务。

线下需要准备串口服务器(tcp转485)、485硬件(继电器)。

控制端则可以是浏览或者App,当然小程序也可以,只要支持websocket的客户端都行。

二、服务器搭建

我们将用上一期学到的swoole建立websocket服务器应用到这里,做到真正的学以致用。然后在这个基础上,再建立一个tcp服务器。

代码相当简单,暂不考虑安全性,稳定性等方面(这些需要大家自己深入,小搜只能把朋友们带进门)。

下面是详细代码:

tcpserver.php

<?php

use TrytoMqtt\Client;
require_once __DIR__ . '/vendor/autoload.php';

$device_maps = [];

//创建Server对象,监听  localhost :39001端口
$serv = new Swoole\ server ("localhost", 39001);
$options = [
 'clean_session' => false,
 'client_id' => 'demo-tcpserver-123456',
 'username' => '',
 'password' => '',
];

$mqtt = new Client('localhost', 1883, $options);

$serv->on('WorkerStart',  function  ($serv, $worker_id) use ($mqtt) {
  echo  "tcp worker start\r\n";
 $mqtt->onConnect = function ($mqtt) {
 $mqtt->subscribe('/websocket485');
 };
 $mqtt->onMessage = function ($topic, $content) use ($serv) {
 global $device_maps;
 var_dump($device_maps);
 echo date('Y-m-d H:i:s') . "Mqtt Receive=================================\r\n";
 var_dump($topic, $content);
 $ json  = json_decode($content, true);
 var_dump($json);
 if (isset($device_maps[$json['device']])) {
 var_dump($json[' msg ']);
 $data = json_decode($json['msg'], true);
 echo "send instruction \n";
 $serv->send($device_maps[$json['device']]['fd'], hex2bin(str_replace(' ', '', $data['instruction'])));
 }
 };
 $mqtt->onError = function ($ exception ) use ($mqtt) {
 echo "error\n";
 };
 $mqtt->on close  = function () {
 echo "close\n";
 };
 $mqtt->connect();
});

//监听连接进入事件
$serv->on('Connect', function ($serv, $fd) {
 echo "Client: Connect.\n";
});

//监听数据接收事件
$serv->on('Receive', function ($serv, $fd, $from_id, $msg) use ($mqtt) {
 global $device_maps;
 var_dump($device_maps);
 echo date('Y-m-d H:i:s') . "=================================\r\n";
 $json = json_decode($msg, true);
 var_dump($json);

 //todo已经注册的TCP串口服务器,不能再注册
 if (is_array($json) && isset($json['reg'])) {
 if (!isset($device_maps[$json['reg']])) {
 $device_maps[$json['reg']] = ['device' => $json['reg'], 'fd' => $fd];
 echo "device register done";
 } else {
 echo "device has been registered";
 }
 } else {
 if (!is_array($json)) {
 foreach ($device_maps as $map) {
 if ($map['fd'] == $fd) {
 var_dump($map);
 $msg = json_encode(['device' => $map['device'], 'msg' => bin2hex($msg)]);
  break ;
 }
 }
 }
 }
 $mqtt->publish('/tcp485', $msg);
});

//监听连接关闭事件
$serv->on('Close', function ($serv, $fd) {
 echo "Client: Close.\n";
 global $device_maps;
 foreach ($device_maps as $key => $map) {
 if ($map['fd'] == $fd) {
 unset($device_maps[$key]);
 echo "remove " . $key;
 break;
 }
 }
});

//启动服务器
$serv->start(); 
 

websocket.php

<?php
use TrytoMqtt\Client;
require_once __DIR__ . '/vendor/autoload.php';
$socket_maps = [];
$server = new swoole_websocket_server("localhost", 39002);
$options = [
 'clean_session' => false,
 'client_id' => 'demo-websocket-123456',
 'username' => '',
 'password' => '',
];
$mqtt = new Client('localhost', 1883, $options);
$server->on('WorkerStart', function ($serv, $worker_id) use ($mqtt) {
 echo "websocket worker start\r\n";
 $mqtt->onConnect = function ($mqtt) {
 $mqtt->subscribe('/tcp485');
 };
 $mqtt->onMessage = function ($topic, $content) use ($serv) {
 global $socket_maps;
 var_dump($socket_maps);
 echo "Mqtt=================================" . date('Y-m-d H:i:s') . "\r\n";
 var_dump($topic, $content);
 $json = json_decode($content, true);
 var_dump($json);
 if (isset($socket_maps[$json['device']])) {
 var_dump($json['msg']);
 echo "response \n";
 $serv->push($socket_maps[$json['device']]['fd'], $json['msg']);
 }
 };
 $mqtt->onError = function ($exception) use ($mqtt) {
 echo "error\n";
 };
 $mqtt->onClose = function () {
 echo "close\n";
 };
 $mqtt->connect();
});
$server->on('open', function ($server, $req) {
 echo "connection open: {$req->fd}\n";
});
$server->on('message', function ($server, $frame) use ($mqtt) {
 global $socket_maps;
 echo "received tcp message: {$frame->data}" . date('Y-m-d H:i:s') . "\n";
 $server->push($frame->fd, $frame->data);
 var_dump($socket_maps);
 $json = json_decode($frame->data, true);
 var_dump($json);
 //已经注册的客户端不能再注册
 if (isset($json['reg'])) {
 if (!isset($socket_maps[$json['reg']])) {
 $socket_maps[$json['reg']] = ['device' => $json['reg'], 'fd' => $frame->fd];
 echo "websocket client register done";
 } else {
 echo "websocket client has been registered";
 }
 $msg = $frame->data;
 } else {
 foreach ($socket_maps as $map) {
 if ($map['fd'] == $frame->fd) {
 var_dump($map);
 $msg = json_encode(['device' => $map['device'], 'msg' => $frame->data]);
 break;
 }
 }
 }
 $mqtt->publish('/websocket485', $msg);
});
$server->on('close', function ($server, $fd) {
 global $socket_maps;
 echo "connection close: {$fd}\n";
 foreach ($socket_maps as $key => $map) {
 if ($map['fd'] == $fd) {
 unset($socket_maps[$key]);
 echo "remove " . $key;
 break;
 }
 }
});
$server->start();
 

三、调试

服务器搭建好后,接着是调试。调试只需要在服运器运行上述两个文件,具体操作请回看往期的文章,小搜这里上一张动图,朋友们看看最终效果。

左边是Websocket客户端,右边是TCP客户端

从上图可以看出,Websocket客户端与TCP客户端已经能够正常通讯。

获取源码 的朋友们可以私信小搜:先 关注 ,然后 私信 小搜,小搜收到后会尽快回复。

下一期我们结合这两个做好了的服务器实现web浏览器控制实际硬件的示例,持续关注小搜将会为大家带来更多实用文章,我们下期再见。

文章来源:智云一二三科技

文章标题:Swoole: TcpServer+SocketServer+EMQTT组合,实现基础设备控制

文章地址:https://www.zhihuclub.com/77624.shtml

关于作者: 智云科技

热门文章

网站地图