swoole踩坑

社会主义新天地 提交于 2020-11-08 05:27:48

前言

最近在做一个项目,需要用到类似通讯,第一想法就是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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!