nginx配置websocket支持wss

在正常业务中经常会碰到服务器需要向客户端发起主动推送数据的需求,其实有很多实现方案,

①、客户端轮询接口
  • 优点:简单
  • 缺点:如果是请求频率比较高,业务场景比较常用,可能会对服务器造成比较大的压力。
②、借用第三方推送服务的静默推送(即消息模式),比如友盟,极光
  • 优点:对服务器压力小,不需要自建长连接服务,移动客户端支持也比较好
  • 缺点:依赖于第三方,受制于人,web版不支持
③、自建长连接服务
  • 优点:提升技能点,锻炼能力,可定制化需求,自由度高
  • 缺点:需要开发时间,配置socket服务,需守护进程保证服务不中断

下面我们就开始研究如何用PHP实现长连接问题:
PHP自身支持socket编程,但是比较繁琐,网上常用的轮子有两种 swoole (c 扩展) 和 workerman(PHPsocket),本文以workerman为例。

1、下载workerman包

workerman官网地址:https://www.workerman.net/workerman
支持直接下载,或者composer安装

2、测试socket连接

首先在把下载的包解压放在php项目里,在根目录建立一个start.php文件

count = 4;

// 当收到客户端发来的数据后返回hello $data给客户端
$ws_worker->onMessage = function($connection, $data)
{
    // 向客户端发送hello $data
    $connection->send('hello ' . $data);
};

// 运行
Worker::runAll();

然后建立html文件,index.html



   
   
   测试websocket
      
        
   
   
   
      
      
   

然后在cmd命令行窗口进入项目目录,运行

php start.php start -d

会看到


image.png

就代表服务以及启动成功了
接下里打开index.html,运行,如果能正常收到页面的alert消息,就代表通讯已经没有问题了。

3、正式业务中如何使用主动推送
上面的例子,我们只是建立好了socket连接,客户端在发送内容到服务器之后能收到返回的消息,这时候我们如何让服务器主动给客户端推送消息呢。实现的思想其实是建立一个对外监听的worker容器,再开启一个内部数据推送监听的端口,再把客户端通过uid做一个映射,通过监听内部端口的数据,来实现把数据转发到对应的映射内的客户端来实现。友盟的推送,laravel的广播功能,都是通过这种逻辑实现的。
下面分别贴一下服务器端服务代码,服务器端推送代码,客户端html代码就可以轻松看明白了。
a、服务代码 start.php

count = 1;
// worker进程启动后建立一个内部通讯端口
$worker->onWorkerStart = function($worker)
{
    // 开启一个内部端口,方便内部系统推送数据,Text协议格式 文本+换行符
    $inner_text_worker = new Worker('Text://0.0.0.0:5678');
    $inner_text_worker->onMessage = function($connection, $buffer)
    {
        global $worker;
        // $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();
// 当有客户端发来消息时执行的回调函数
$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;
       $connection->send($data);
       return;
    }
};

// 当有客户端连接断开时
$worker->onClose = function($connection)use($worker)
{
    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();

b、推送代码 push.php

'uid4', 'percent'=>'88%');
// 发送数据,注意5678端口是Text协议的端口,Text协议需要在数据末尾加上换行符
fwrite($client, json_encode($data)."\n");
// 读取推送结果
echo fread($client, 8192);

c、客户端HTML index.html



   
   
   uid4的接收页面(修改uid即可测试给哪个客户端推送)
      
   
   
      
测试页面

先启动服务,再打开网页,最后运行push.php即可测试,这里比较关键的是进程数必须设置为1,否则可能无法推送成功。一个基础的长连接推送就这样ok了。
如果需要多进程啦、服务器集群啦、就需要基于Channel组件或者GatewayWorker了,更多进阶功能可以参考官方文档http://doc.workerman.net

wss的nginx服务器配置

话不多说粘贴配置,这个放在https的配置里面

        location /wss
        {
            proxy_pass http://127.0.0.1:2345;
            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;
        }

接下来吧前端页面的代码做修改

var ws = new WebSocket("wss://api.pinkechuxing.com/wss");

就是这么简单就配置好了

在实际的使用中我们可能会遇到连接中断的情况,这个时候就需要发送心跳包来维持连接

var ws = new WebSocket("ws://www.goozp.com");

//连接websocket
ws.onopen = function () {
    setInterval(function () {
        ws.send('Hello!');
    }, 10000)
};

你可能感兴趣的:(nginx配置websocket支持wss)