workerman 实现 websocket

概述

PHP使用workerman框架的websocket, 实现机器扫码检票, 异步刷新小程序页面的票状态.

首先,需要小程序前端页面使用openid作为标志(代码里是$uid)连接websocketServer

然后, 检票接口做完检票逻辑后, 将结果通过websocketClient推送到小程序页面

最后, 小程序根据接收到的推送消息处理页面

 

一. 什么是websock?

参考《看完让你彻底搞懂Websocket原理》(https://www.cnblogs.com/fuqiang88/p/5956363.html

深入浅出地介绍了websock的原理和作用,通俗易懂,膜拜大神。

 

二. 什么是workerman

实际上Workerman类似一个PHP版本的nginx,核心也是多进程+Epoll+非阻塞IO。Workerman每个进程能维持上万并发连接。由于本身常住内存,不依赖Apache、nginx、php-fpm这些容器,拥有超高的性能。同时支持TCP、UDP、UNIXSOCKET,支持长连接,支持Websocket、HTTP、WSS、HTTPS等通讯协以及各种自定义协议。拥有定时器、异步socket客户端、异步Mysql、异步Redis、异步Http、异步消息队列等众多高性能组件。

官网地址(http://doc.workerman.net/)

 

三. Nginx配置wss

ubuntu@VM-156-73-ubuntu:/usr/local/nginx/conf/vhost$ more test.com.cn.conf
upstream websocket1{
    server localhost:19988;
}

server
    {
        listen 80;
        listen 443;
        #listen [::]:80;
        server_name test.com.cn ;
        index index.html index.htm index.php default.html default.htm default.php;
        root  /data/www/zc2api.zhongchengbus.com.cn/public;

        charset utf-8;

        ssl on;
        ssl_certificate /usr/local/nginx/conf/cert/1_test.com.cn_bundle.crt;
        ssl_certificate_key /usr/local/nginx/conf/cert/2_test.com.cn.key;
        ssl_session_timeout 5m;
        ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
        ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:HIGH:!aNULL:!MD5:!RC4:!DHE;
        ssl_prefer_server_ciphers on;

        include other.conf;
        #error_page   404   /404.html;

        # Deny access to PHP files in specific directory
        #location ~ /(wp-content|uploads|wp-includes|images)/.*\.php$ { deny all; }

        include enable-php.conf;

        location ~ .*\.(gif|jpg|jpeg|png|bmp|swf)$
        {
            expires      30d;
        }

        location ~ .*\.(js|css)?$
        {
            expires      12h;
        }

        location /wss
        {
                proxy_pass http://websocket1;
                proxy_http_version 1.1;
                proxy_set_header Upgrade $http_upgrade;
                proxy_set_header Connection "upgrade";
                proxy_set_header X-real-ip $remote_addr;
                proxy_set_header HOST $host;
                proxy_set_header X-Forwarded-For $remote_addr;
         }

        location ~ /.well-known {
            allow all;
        }

        location ~ /.git/
        {
            deny all;
        }

        location ~ /\.
        {
            deny all;
        }

        #access_log /data/www/test.com.cn/logs/access.log;
        error_log /data/www/test.com.cn/logs/error.log;
    }

 

四. websocket服务端实现websocketSever.php

<?php
/**
 * 使用workerman异步实现扫码检票刷新小程序页面状态
 * 大体逻辑:
 *  1.[小程序] 页面通过端口19987与 [此服务程序] 建立socket长连接,并传递order_no(或者openid)过来映射连接
 *  2.[扫码设备] 或人工扫码检票后,通过建立socket长连接请求本服务程序的19989端口,并传递order_no(或者openid)过来,回调指定页面
 */
use Workerman\Worker;
require_once __DIR__.'/Workerman/Autoloader.php';

//初始化一个worker容器,监听19988端口
$worker = new Worker('websocket://0.0.0.0:19988');

/**
 * 这里进程数必须设置为1,否则会报端口占用错误
 * (php7 可以设置进程数大于1,前提是$inner_worker->reusePort=true)
 */
$worker->count = 1;

//$worker进程启动后创建一个text Worker,以便打开一个内部通讯端口
$worker->onWorkerStart = function($worker)
{
    //开启一个内部端口,方便内部系统推送数据,Text协议格式 文本+换行符
    $inner_text_worker = new Worker('text://0.0.0.0:19989');
    $inner_text_worker->onMessage=function($connection,$buffer)
    {
        //$data数组格式,里面有uid,表示向那个uid的页面推送数据
        $data = json_decode($buffer,true);
        $uid = $data['uid'];

        //通过workerman,向uid的页面推送数据
        $ret = sendMessageByUid($uid,$buffer);

        //返回推送结果
        $connection->send($ret?'ok':'fail');
    };
    $inner_text_worker->listen();
};

//新增一个属性,用来保存uid到connection的映射
$worker->uidConnections = array();

//当有客户端发来消息时执行的回调函数(微信小程序的话,可以接受order_no(或者openid)作为uid传过来)
$worker->onMessage = function($connection,$data)
{
    global $worker;
    $date = date('Y-m-d H:i:s',time());
    file_put_contents('./workerman.log',$date.PHP_EOL,FILE_APPEND );
    file_put_contents('./workerman.log',$data.PHP_EOL,FILE_APPEND );
    file_put_contents('./workerman.log',$date.PHP_EOL,FILE_APPEND );
    $data = json_decode($data,true);

    if(!isset($connection->uid))
    {
        //没验证的话把第一个包当做uid
        $connection->uid = $data['uid'];

        //保存uid到connection映射,这样可以方便的通过uid查找connection,实现针对特定的uid推送数据
        $worker->uidConnections[$connection->uid] = $connection;
        return;
    }
};

//当有客户端连接断开时,删除映射
$worker->onClose = function($connection)
{
    global $worker;
    if(isset($connection->uid))
    {
        //连接断开时,删除映射
        unset($worker->uidConnections[$connection->uid]);
    }
};

//向所有验证的用户推送数据
function broadcast($message)
{
    global $worker;
    foreach($worker->uidConnections as $connection)
    {
        $connection->send($message);
    }
}

//针对uid推送数据
function sendMessageByUid($uid,$message)
{
    global $worker;
    if(isset($worker->uidConnections[$uid]))
    {
        $connection = $worker->uidConnections[$uid];
        $connection->send($message);
        return true;
    }
    return false;
}

//运行所有worker
Worker::runAll();

 

五. 客户端实现 websocketClient.php

注:检票接口处理检票逻辑后调用, 将检票结果推送到小程序

<?php
//建立socket连接到内部推送端口
$client = stream_socket_client('tcp://127.0.0.1:19989',$errno,$errmsg,1);

//推送的数据,包含uid字段,表示是给这个uid推送
$data = array('uid'=>'oskf64usrJ8-Lk99ZYAI-zd3EugM','status' => 'success','msg' => '检票成功');//检票之后传过来的数据

//发送数据,注意19989端口是text协议的端口,text协议需要在数据末尾加上换行符
fwrite($client,json_encode($data)."\n");

//读取推送结果
echo fread($client,8192);

 

六. 前端页面实现

  (1)小程序前端

  注:小程序已经自动实现了心跳传输

//建立websocket连接//
  startWebSocket: function () {
    var that = this;
    that.WebSocketInit()
    wx.onSocketError(function () {
    })
    wx.onSocketMessage(function (data_) {
      //that.queryUnCheckinTicketList();
    })
    wx.onSocketClose(function (res) {
      that.WebSocketInit()
    })
  },

  WebSocketInit: function () {
    var that = this;
    wx.connectSocket({
      url: that.data.wws,//wss://test.com.cn/wss
      data: {},
      method: 'GET',
      success: function (res) {
        console.log("connectSocket 成功")
      },
      fail: function (res) {
        console.log("connectSocket 失败")
      }
    })

  
    wx.onSocketOpen(function () {
      // callback
      var uid = that.data.openid;
      console.log('uid '+uid);
      var mCmd = { "uid": uid }
      wx.sendSocketMessage({
        data: JSON.stringify(mCmd),
        success: function (res) {
          console.log("sendSocketMessage 成功")
        },
        fail: function (res) {
          console.log("sendSocketMessage 失败")
        }
      });

      wx.onSocketMessage(function (data) {
        console.log("onSocketMessage ", data)
        that.queryUnCheckinTicketList();
      })


    })
  },

 

(2)jQuery前端

  注: 这段代码可以用来测试websocket服务是否成功

ws = new WebSocket("wss://test.com.cn/wss");
ws.onopen = function() {
    alert("连接成功");
    ws.send('tom');
    alert("给服务端发送一个字符串:tom");
};
ws.onmessage = function(e) {
    alert("收到服务端的消息:" + e.data);
};

 

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 9
    评论
评论 9
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值