前言
最近在做一个项目,需要用到类似通讯,第一想法就是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
来源:oschina
链接:https://my.oschina.net/u/2971783/blog/2051600