swoole中的websocket的如何发给指定人消息.

swoole版本:1.9.2
需求: 类似直播的评论功能, 比如A房间下面显示A房间的评论 B房间下面显示B房间的评论.... 这个应该是个正常的需求吧.. 但是我现在发现一个问题, 就是 消息是发给所有在线链接者.. 就是相当于A下面的评论在 BCDEF 都能看到.. 这就很蛋疼了... 不知道啥问题.. 求各位大哥大姐帮帮忙看看. 公司很急呀

代码如下:

<?php
$ws = new Swoole\Websocket\Server("0.0.0.0", 9508);

//监听WebSocket连接打开事件
$ws->on('open', function ($ws, $request) {

    $fd = $request->fd;
    echo "client-{$fd} is connect\n";
    //$ws->push($request->fd, "hello, welcome\n");
});

//监听WebSocket消息事件
$ws->on('message', function ($ws, $frame) {

    // $msg =  'from'.$frame->fd.":{$frame->data}\n";
    // $data   = htmlentities(htmlspecialchars($frame->data));
    // if(mb_strlen($data,'utf8')>30){
    //     $data = mb_substr($data, 0,30,'utf-8').'...';
    // }
    $data = json_decode($frame->data,true);
     //判断是否有礼物
    if($data['gift']){
        $gift = json_decode($data['gift'],true);
        $arr = array(
        'info'   => $data['info'],
        'uid'    => $data['uid'],
        'username' =>$data['username'],
        'imageSrc' =>$gift['img'],
        'number' =>$gift['number'],
    );

    }else{

    // 正常发送
    $arr = array(
        'info'   => $data['info'],
        'uid'    => $data['uid'],
        'username' =>$data['username'],
        'href'   => 'javascript:void(0);',
        'status' => 1
    );
    }

    $msg = json_encode($arr);

    foreach($ws->connections as $fd) {
        //发送信息
        $ws->push($fd, $msg);
    }


});

//监听WebSocket连接关闭事件
$ws->on('close', function ($ws, $fd) {
    echo "client-{$fd} is closed\n";
});

$ws->start();
阅读 10.3k
5 个回答

发送到指定的人,必须知道指定的人的fd,可以在用户登录的时候将uid和fd做个关联存到redis或者swoole_table等其他共享内存中,通过uid找到fd进而push到这个fd上。

你看你代码中的

foreach($ws->connections as $fd) {
        //发送信息
        $ws->push($fd, $msg);
    }

其中$ws->connections是获取所有的文件描述符,你如果要A直播间能看到A的评论,B能看到B的评论,你应该对fd进行分组,诸如使用redis将所有在A直播间下的fd保存到一个集合中。在A直播间下进行的评论,只取出A直播间下的fd.
举个例子,伪代码:

//当用户进入A直播间
$redis->sadd('RoomA', $fd);

给A直播间下的人发送消息

//获取所有属于A直播间下的用户的fd
$clients = $redis->smembers('RoomA');
foreach($clients as $key => $fd) {
    $ws->push($fd, $msg);
}

没实践过,说个思路:把连接按房间存起来,发送消息时只发对应房间里的连接

open:用户连接
message join_room:用户进入房间,把用户连接放到房间对应的数组
message 其他消息:遍历用户所在房间的连接,把消息转发给发送给他们
close: 关闭,把用户连接移除房间

业务有房间的概念,就应该有进入/离开房间的message,和保存房间的状态数据结构。

看你保存的发给你fd和uid对应的数据在哪里。。发给那个fd就是发个那个用户了

一般不是用swoole创建一个socket服务吗,创建个http服务是不是浪费了点时间,正如上面说的每个连接swoole的client,swoole会分配各fd标识,把标识存到redis等中(做个用户关联),服务端根据客户端的fd去判断当前的通信来源,就可以做对应关系了

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题
宣传栏