[Websocket]如何向指定终端发送消息?

火红的鲤鱼

我启动了一个websocket服务(websocket协议),然后打开了浏览器,打开了两个浏览器标签页与其建立连接(通过js原生的Websocket对象),然而,实际上我发现connections只有一个,这导致我无法向指定的标签页发送信息。
请问,如何向指定的客户端发送信息?
 ----------------------------分割线----------------------------
 
我是按照如下方式发现多个标签页用的是同一个connection的。
首先,在join或login时打印workerId和$connection->id拼成的字符串(logEvent是我自定义的日志输出函数)

[attach]1866[/attach]

[attach]1867[/attach]
 
然后,打开两个浏览器标签页各自建立连接

[attach]1870[/attach]

[attach]1871[/attach]

 
最后,查看服务端日志信息
 Workerman start in DEBUG mode
------------------------------------------- WORKERMAN --------------------------------------------
Workerman version:3.5.18 PHP version:7.2.8
-------------------------------------------- WORKERS ---------------------------------------------
proto user worker listen processes status
tcp www-data none websocket://0.0.0.0:2346 4

Press Ctrl+C to stop. Start success.
new connection is coming...172.18.0.1
2019-04-01 13:43:08:
2019-04-01 13:43:08:
2019-04-01 13:43:08:
2019-04-01 13:43:08:
2019-04-01 13:43:08:
-----join over

2019-04-01 13:43:08:
2019-04-01 13:43:08:
2019-04-01 13:43:08:
2019-04-01 13:43:08:
2019-04-01 13:43:08:
2019-04-01 13:43:08:
-----login over

new connection is coming...172.18.0.1
2019-04-01 13:43:13:
2019-04-01 13:43:13:
2019-04-01 13:43:13:
2019-04-01 13:43:13:
-----join over

2019-04-01 13:43:14:
2019-04-01 13:43:14:
2019-04-01 13:43:14:
2019-04-01 13:43:14:
2019-04-01 13:43:14:
2019-04-01 13:43:14:
-----login over

可以看到,两次连接,经过了两轮join/login,但是每次的workerId_$connection->id组合都是一致的,都是0000000045562dda0000000048257f4c_1但是官方文档上说,http://doc.workerman.net/tcp-connection.html,所以,是我使用姿势不对么?请指教

6663 1 0
1个回答

phpcreeper

使用姿势不对:
不能使用 workerId 【用于区分不同的worker实例】, 至少使用 $worker->id  是正确的【用于区分同一个worker实例的不同进程】。

  • 火红的鲤鱼 2019-04-01

    但是我用了$worker->id拿到worker的id之后,怎么获得那个worker的实例呢?我之前是用Worker::getAllWorkers()获取了worker数组,然后发现它的键其实是workerId。

  • 火红的鲤鱼 2019-04-01

    其实我是想让标签页1给标签页2发送一个websocket消息,所以我就得获取用于标签页2的那个$connection实例。

  • phpcreeper 2019-04-01

    两个页面发起的websocket连接分别是两个不同的连接对象,而 $connection->worker 就是获得当前连接所属的worker实例,所谓的两个标签页要进行互相推送消息,就是不同的连接对象就行通信的过程,所以我们只要确定出不同的唯一的连接对象uid,比如可以这样使用:
    $uid = $conneciton->worker->id . $connection->id;

    另外注意: 单进程和多进程的通信手段是不一样的,具体怎么玩手册上都有特别说明。

  • 火红的鲤鱼 2019-04-03

    多谢,我找到这个问题的解决方案了,在这个页面上:http://doc.workerman.net/components/channel-examples.html

    总结一下,在workerman里面,由于每个connection保持一个连接,所以如果你要针对某个客户端发送消息的话,实际上是要通过那个特定的connection去发送这个消息,而connection又被worker持有,所以你得先找到那个worker,再找到那个connection,然后才能发送消息。

    要调动某个特定的worker,需要做进程间通信,因为你不能确定当前负责处理消息任务的那个worker就是持有那个connection的worker。workerman有一个ChannelServer组件,就是在上面链接的那个页面中,利用这个ChannelServer可以做到进程间通信,具体流程就是:

    1、广播指令到达
    2、向监听了指定worker->id的ChannelClient发送消息(这里为什么要监听worker->id而不是别的,因为这样可以节省好多流量和cpu操作,要不然,你的每个worker都要响应一下这个消息事件来查看这个消息是不是发给自己的)
    3、根据发送的消息数据来确定由哪个connection处理该消息
    4、调用指定connection的send方法(完成广播,其实广播就是循环调用一批connection)

    ——————————————分割线————————————————

    再说一下我的误区。

    之前查看文档说,每个连接都由一个connection保持,然后我在发送广播的时候,首先冒出的第一个想法就是,那我得先找到那个connection(这个想法没错),然后我发现是worker持有connection,然后就得找到worker,才能找到connection(到这里,想法也没错)。

    然后由于服务是多进程的,不能确定是被哪个worker处理了,所以,我的想法是建立一个数据共享机制,让无论在哪个worker中都可以获得某个指定的worker实例(这里开始有问题了),所以我使用了GlobalData组件,不管哪个worker收到消息,都把自身的id在这个共享库中注册一下,然后当广播消息到达时,我就去这个共享库中找那个worker,然后找到那个worker中的那个特定的connection。

    表面上看,这样做貌似没毛病,但是有个很大的错误,就是,就算我把所有worker的id都注册到了数据共享库中,我也无法直接在某个进程worker中操作别的进程worker,因为进程之间是互相隔离的-_-!!。

    所以就需要进程间通信了,所以就需要ChannerServer。

    ——————————————分割线————————————————

    按照我那种错误的做法,实际表现出来的结果是,数据共享库中一直只保存了一个wokerId+connection->id组合。

年代过于久远,无法发表回答
×
🔝