Websocket实现WSS

5,153 阅读1分钟

注意点:

  1. 如果网站使用HTTPS,WebSocket必须要使用wss协议
  2. 使用wss协议的连接请求必须只能写域名,而非IP+端口
  3. 通过nginx转发实现wss,内部通信就不需要使用wss了

Nginx 配置

  1. 只需要在HTTPS配置的server内加一个location即可
  2. Nginx反向代理,无论是HTTP/S或是WebSocket都会走443端口,由Nginx分发给各个项目服务器
# 代理方式实现
location = /websocket {
    proxy_pass http://xxxx.cn:7303;

    proxy_redirect off;
    proxy_set_header Host www.xxx.cn:7303;

    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 X-Forwarded-For $remote_addr;
}

Laravel

Server

<?php

namespace App\Console\Commands;

use Illuminate\Console\Command;

class WebSocket extends Command
{
    /**
     * The name and signature of the console command.
     *
     * @var string
     */
    protected $signature = 'Web {action=start}';

    /**
     * The console command description.
     *
     * @var string
     */
    protected $description = 'WebSocket description';

    /**
     * Create a new command instance.
     *
     * @return void
     */
    public function __construct()
    {
        parent::__construct();
    }

    /**
     * Execute the console command.
     *
     * @return mixed
     */
    public function handle()
    {
        $arg = $this->argument('action');
        switch ($arg) {
            case 'start':
                $this->info('WebSocket observer started');
                $this->start();
                break;
        }
    }

    public function start()
    {
        // ssl方式
        //$server = new \swoole_websocket_server("0.0.0.0", 7303, SWOOLE_PROCESS, SWOOLE_SOCK_TCP | SWOOLE_SSL);
        $server = new \swoole_websocket_server("0.0.0.0", 7303);

        $server->set([
            //后台作为守护进程运行 'daemonize' => 1
            'daemonize' => 1,
            //配置SSL证书和密钥路径
            //'ssl_cert_file' => "/data/php/cert/www.xxxxx.cn.pem",
            //'ssl_key_file'  => "/data/php/cert/www.xxxxx.cn.key"
        ]);

        $server->on('Open', array($this, 'OnOpen'));
        $server->on('Message', array($this, 'OnMessage'));
        $server->on('Close', array($this, 'OnClose'));
        $server->start();
    }

    public function OnOpen($server, $req)
    {
        echo "server: handshake success with fd {$req->fd}\n";
    }

    public function OnMessage($server, $frame)
    {
        var_dump($frame);
        $this->send($server, $frame);
    }

    public function OnClose($server, $fd)
    {
        echo "client {$fd} closed \n";
    }

    public function send($server, $frame)
    {
        $info = json_decode($frame->data, true);
        // var_dump(php_sapi_name());
        switch ($info['action']) {
            case 'home':
            case 'device':
            case 'goodsError':
            case 'shopping':
                $conn_list = $server->connection_list(0, 100);
                if ($conn_list === false or count($conn_list) === 0) {
                    echo "finish \n";
                }
                foreach ($conn_list as $fd) {
                    if ($fd != $frame->fd) {
                        $arr = array("action" => $info['action'], "data" => $info['data']);
                        $data = json_encode($arr);
                        $server->push($fd, $data);
                    }
                }
                break;
        }
    }
}

Client

<?php

namespace App\Console\Commands;

use Illuminate\Console\Command;

class WebClient extends Command
{
    /**
     * The name and signature of the console command.
     *
     * @var string
     */
    protected $signature = 'SendSocket {data}';

    /**
     * The console command description.
     *
     * @var string
     */
    protected $description = 'WebClient description';

    /**
     * Create a new command instance.
     *
     * @return void
     */
    public function __construct()
    {
        parent::__construct();
    }

    /**
     * Execute the console command.
     *
     * @return mixed
     */
    public function handle()
    {
        $data = $this->argument('data');

        $cli = new \swoole_http_client('127.0.0.1', 7303);

        $cli->setHeaders(['Trace-Id' => md5(time()),]);

        $cli->on('message', function ($_cli, $frame) {
            var_dump($frame);
        });

        $cli->upgrade('/', function ($cli) use ($cli, $data) {
            $cli->push($data);
            $cli->close();
        });
    }
}

WEB

# 地址后面必须加 /websocket
let ws = new WebSocket("wss://www.xxx.com:7300/websocket');

ws.onopen = function(evt) {
    console.log("Connection open ...");
    // ws.send("Hello WebSockets!");
};


// 在指定页面接取到消息指定刷新操作
ws.onmessage = function (evt) {
    let hashValue = window.location.hash.split('/');
    let hashLength = hashValue . length;
    let newKey = hashValue[hashLength - 1];
    console . log("Received Message: " + evt . data);
    // ws.close();
    let data = JSON . parse(evt . data);
    if (data.action === 'home' && newKey === '') {
        console . log('home-socket');
        //home页监控
        $this.homeSocket();
    } else if (data.action === 'home' && newKey === 'shoppingMonitor') {
        console.log('shopping-home-socket')
        //购物车监控
        $this.shoppingMonitorSocket();
    } else if (data . action === 'shopping' && newKey === 'shoppingMonitor') {
        console.log('shopping-socket');
        //购物车监控
        $this . shoppingMonitorSocket();
    } else if (data . action === 'goodsError') {
        console.log('goodsError-socket');
    }
}  

ws.onclose = function(e){
  //当客户端收到服务端发送的关闭连接请求时,触发onclose事件
  console.log("close");
}

ws.onerror = function(e){
  //如果出现连接、处理、接收、发送数据失败的时候触发onerror事件
  console.log(error);
}