swoole踩坑 原

前言

最近在做一個項目,需要用到類似通訊,第一想法就是socket,然後一頓百度發現了swool這個東西,之前有在項目中使用過workman,心想應該是差不多的吧,但是後面才發現兩者其實還是有挺大差異的

安裝

workman是一個類似packages的東西,我們之間使用composer來安裝就可以了

swoole不一樣,他是官方插件,所以我們需要用平時安裝php插件的方法來安裝

使用

官方那邊有很詳細的文檔,我在這裏就不一個一個說了,就說我遇到的一些坑把

*多個線程之間的變量共享,不能使用global,而要用官方的swoole_table

我的項目裏面,是需要在一個server文件裏面啓動兩個端口監聽的,一個用來和瀏覽器溝通的websocket,一個是和內部溝通的普通TCP,而這兩者之間是需要數據交流的,我之前在使用workman的時候是這樣使用的

use Workerman\Worker;
use PHPSocketIO\SocketIO;

// 用戶組(記錄所有在線的用戶)
$userList = array();
$socket = new SocketIO(9527);

$socket->on('connection', function ($socket) {
   // 聲明userList是全局變量
   global $userList;
   // 客戶端連接的時候用一個標識標識這個連接
   $userId = $socket->handshake['query']['userId'];
   // 加到數組裏面去
   $userList[] = $userId;
});


// 當$sender_io啓動後監聽一個http端口,通過這個端口可以給任意web客戶端發送信息
$socket->on('workerStart', function () {
   global $socket,$userList;
   // 監聽一個http端口
   $inner_http_worker = new Worker(9523);
   // 當http客戶端發來數據時觸發
   $inner_http_worker->onMessage = function ($http_connection, $data) {
       global $allSocket;
       $_POST = $_POST ? $_POST : $_GET;
       $to = @$_POST['to'];
       $toId = $userlist['to']
       $name = @$_POST['name'];
       $_POST['data'] = htmlspecialchars(@$_POST['data']);
       // 有指定uid則向uid所在socket組發送數據
       if ($to) {
           $allSocket->to($toId)->emit($name, $_POST['data']);
           // 否則向所有uid推送數據
       } else {
           $allSocket->emit($name, $_POST['data']);
       }


       return $http_connection->send('ok');
   };
   // 執行監聽
   $inner_http_worker->listen();

});

上面的代碼可以看到,我在兩個不同的端口server裏面是可以用global通訊的

但是swool就不可以了,下面是swool的代碼

// 因爲有兩個線程之間無法溝通所以需要用這個來溝通
$table = new swoole_table(1024);
$table->column('userList', swoole_table::TYPE_STRING, 512);
$table->create();


function addUser($fd, $id)
{
    global $table;

    $userList = getUserList();
    $userList[$id] = $fd;

    $table->set('1', ['userList' => json_encode($userList)]);

    print_r($id . '上線了');

    return $userList;
}

function delUser($fd, $id)
{
    global $table;
}

function getUserList()
{
    global $table;

    if ($table->exist('1')) {
        $userList = json_decode($table->get('1')['userList'], true);
    }

    return $userList;
}

function getUser($id)
{
    global $table;

    $userList = getUserList();


    return $userList[$id];
}

// 用於和外部客戶端溝通
$socketServer = new swoole_websocket_server("0.0.0.0", $params['swoole']['stockPort']);
// 用於和內部溝通
$tcpServer = $socketServer->addlistener("127.0.0.1", $params['swoole']['tcpPort'], SWOOLE_SOCK_TCP);

$socketServer->on('open', function (swoole_websocket_server $server, $request) {
    echo "server: handshake success with fd{$request->fd}\n";
});

$socketServer->on('handshake', function (swoole_http_request $request, swoole_http_response $response) {
    global $socketServer;

    if (!isset($request->header['sec-websocket-key'])) {
        //'Bad protocol implementation: it is not RFC6455.'
        $response->end();
        return false;
    }
    if (0 === preg_match('#^[+/0-9A-Za-z]{21}[AQgw]==$#', $request->header['sec-websocket-key'])
        || 16 !== strlen(base64_decode($request->header['sec-websocket-key']))
    ) {
        //Header Sec-WebSocket-Key is illegal;
        $response->end();
        return false;
    }

    $key = base64_encode(sha1($request->header['sec-websocket-key']
        . '258EAFA5-E914-47DA-95CA-C5AB0DC85B11',
        true));
    $headers = array(
        'Upgrade' => 'websocket',
        'Connection' => 'Upgrade',
        'Sec-WebSocket-Accept' => $key,
        'Sec-WebSocket-Version' => '13',
        'KeepAlive' => 'off',
    );
    foreach ($headers as $key => $val) {
        $response->header($key, $val);
    }
    $response->status(101);
    $response->end();

    $fd = $request->fd;
    $id = base64_decode($request->get['id']);

    if ($fid = getUser($id)) {
        // 這個用戶之前登陸過,發送消息給那個用戶讓其退出登錄
        $socketServer->push($fid, 'logout');
    }

    addUser($fd, $id);

    $socketServer->defer(function () use ($fd, $socketServer) {
        $socketServer->push($fd, "hello, welcome\n");
    });
    return true;
});


$socketServer->on('message', function (swoole_websocket_server $server, $frame) {
    echo "receive from {$frame->fd}:{$frame->data},opcode:{$frame->opcode},fin:{$frame->finish}\n";
    $server->push($frame->fd, "this is server");
});
$socketServer->on('close', function ($ser, $fd) {
    echo "client {$fd} closed\n";
});


$tcpServer->set([]);
// 接收到內部發送的請求
$tcpServer->on("receive", function ($serv, $fd, $threadId, $data) {
    global $socketServer, $table;
    print_r($data);
    $data = json_decode($data, true);
    $userId = $data['userId'];
    $method = $data['method'];
    $params = $data['params'];
    $json = [
        'method' => $method,
        'params' => $params
    ];
    $sendData = json_encode($json);
    $fd1 = getUser($userId);
    $socketServer->push($fd1, $sendData);
    $serv->close($fd);
});
// 內部請求完畢以後關閉
$tcpServer->on("close", function ($ser, $fd) {
    print_r($fd . '被關閉');
    print_r('11');

});

$socketServer->start();

可以看到,我必須使用swoole_table這個東西來協助我

socket和websocket

從上面的代碼看,workman我用的是socket,之前我在swool的時候,看到有websocket的代碼示例,然後我在客戶端那邊還是用我之前的socket.io來鏈接,發現怎麼都不行,一直斷,後面我才知道,這兩者是不一樣的東西,socket和websocket的鏈接和api都是不一樣的,websocket必須使用new WebSocket來鏈接,下面這篇文章寫得還是不錯的

https://blog.csdn.net/wwd0501/article/details/54582912

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章