Workman实现多协议控制设备的两种方案

流氓凡 技术分享 2021-09-16 404 0

需求简述

微信小程序控制“合宙Air724UG”模块发送指令。

可能遇到的问题

  1. 微信小程序必须使用wss://协议连接websocket;

  2. 硬件设备使用tcp协议,小程序使用websocket协议,多协议互通是个要解决的问题;

  3. 多协议之间的设备信息交互问题;


解决方案

  1. 使用Nginx反向代理做wss://实现

  2. 目前有两种方案:

    1. 一个是只搭建tcp协议,微信小程序跑服务端http接口,然后将服务端当做客户端使用tcp协议连接webscoket并传输数据。

    2. 搭建websocket并在这个进程上开一个tcp协议(推荐此方法),微信小程序使用wss协议连接websocket服务

    3. 两个方案设备信息数据交互基本都是定义一个global变量在里面传递(需要注意的是,进程必须是1个,否则传递会有问题


代码示例Demo

首先是直接搭建一个tcp协议的websocket

        $address = '0.0.0.0';
        $port = 39210;
        $ws_worker = new Worker("tcp://" . $address . ":" . $port);
        // 进程数必须是1
        $ws_worker->count = 1;
        // 当收到客户端发来的数据后返回hello $data给客户端
        $ws_worker->onMessage = function ($connection, $data) {
            // 通过全局变量获得db实例
            $resultData = json_decode($data, true);
            if (isset($resultData['imei']) && isset($resultData['iccid'])) { // 设备进入
                $this->device[$resultData['imei']] = $connection; // 设备列表,用于后续发送数据
            } else { // 用户进入准备设备控制流程
                if (isset($resultData['id']) && isset($resultData['command']) && isset($this->device[$resultData['id']])) { // 设备发送指令
                    $sendServer = $this->device[$resultData['id']];
                    $msg = $sendServer->send($resultData['command'] . "\r\n");
                    if ($msg) {
                        $connection->send(json_encode(['code' => 1, 'msg' => '发送成功', 'data' => $resultData]));
                    } else {
                        $connection->send(json_encode(['code' => 0, 'msg' => '消息发送失败', 'data' => $resultData]));
                    }
                }

                if (isset($resultData['id']) && isset($resultData['command']) && !isset($this->device[$resultData['id']])) { // 未找到对应设备
                    $connection->send(json_encode(['code' => 0, 'msg' => '系统繁忙,请稍后再试', 'data' => ['num' => count($this->device)]]));
                }
            }

        };
        // Emitted when connection closed
        $ws_worker->onClose = function ($connection) {
            //            echo "Connection closed\n";
        };
        // 运行worker
        Worker::runAll();

服务端提供接口发送tcp数据实现设备控制

    $st = [
        'id' => 1111, // 设备识别码
        'command' => '1212', // 发送的指令
    ];
    $st = json_encode($st);
    $length = strlen($st);
    //创建tcp套接字
    $socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
    //连接tcp
    socket_connect($socket, '你的ip', 39210);
    //向打开的套集字写入数据(发送数据)
    $s = socket_write($socket, $st, $length);
    //从套接字中获取服务器发送来的数据
    $msg = socket_read($socket, 8190);
    $result = $msg;
    //关闭连接
    socket_close($socket);
    return json_decode($result, true);

到此为止了,就是这么简单,多设备调试下即可使用。好处就是微信小程序不需要配置wss


多协议websocket实现

        global $ws_worker;
        // websocket 协议的worker
        $ws_worker = new Worker('Websocket://0.0.0.0:39211');
        // 这里只能写1
        $ws_worker->count = 1;
        $ws_worker->d = [];
        // websocket server 启动后在当前进程增加一个socket监听
        $ws_worker->onWorkerStart = function ($ws_worker) {
            $socket_worker = new Worker('tcp://0.0.0.0:39210');
            // 当设备发来数据时如何处理
            $socket_worker->onMessage = function ($connection, $data) {
                // 这里处理设备发来的数据 $data
                global $ws_worker;
                // 比如像这样给所有的WebSocket连接转发数据
                if ($data) {
                    $resultData = json_decode($data, true);
                    $this->device[$resultData['imei']] = $connection;
                    $this->deviceOnline[$resultData['imei']] = $resultData;
                    $ws_worker->d[$resultData['imei']] = $connection;
                }
            };
            $ws_worker->socketWorker = $socket_worker;
            // 执行监听
            $socket_worker->listen();
        };

        // websocket协议也就是浏览器发来数据时
        $ws_worker->onMessage = function ($connection, $data) {
            global $ws_worker;
            if (!$data) {
                $connection->send(json_encode(['code' => 0, 'msg' => '无发送内容', 'data' => []]));
            } else {
                $resultData = json_decode($data, true);
                if (isset($resultData['id']) && isset($resultData['command']) && isset($ws_worker->d[$resultData['id']])) { // 设备发送指令
                    $sendServer = $ws_worker->d[$resultData['id']];
                    $msg = $sendServer->send($resultData['command'] . "\r\n");
                    if ($msg) {
                        $connection->send(json_encode(['code' => 1, 'msg' => '发送成功', 'data' => $resultData]));
                    } else {
                        $connection->send(json_encode(['code' => 0, 'msg' => '消息发送失败', 'data' => $resultData]));
                    }
                }
                if (isset($resultData['id']) && isset($resultData['command']) && !isset($ws_worker->d[$resultData['id']])) { // 未找到对应设备
                    $connection->send(json_encode(['code' => 0, 'msg' => '设备离线', 'data' => []]));
                }
            }
        };
        Worker::runAll();

这样的话执行 php ws_start start -d 监听服务后,小程序端发送的数据格式就是json即可,比如

{id:111,command:222}

接下来配置下Nginx转发wss:

    #SSL-END
    #wss协议转发 小程序里面要访问的链接
    # 访问:wss://xxxx.com/wss
    location /wss
    {
        proxy_pass http://127.0.0.1:39211;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header Host $host;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        rewrite /wss/(.*) /$1 break;
        proxy_redirect off;
    }


没有更多要说的了。



评论