当前的状态是客户端主动给服务端发消息,服务端在onMessage里面给客户端发消息是OK的,IP和端口已经保存,并且客户端每上传一条信息就自动更新ip和port,目前的问题点是,需要在onMessage外面,通过api的形式给客户端发消息,请问是否有成熟可用的案例或者思路?
目前服务端是:workerman建udp和websocket服务端,udp连接设备,websocket连接用户侧,udp将收集的数据通过websocket发给用户,websocket实时的和服务器进行交互获取最新数据,webman则提供控制api,需要对设备进行控制的命令用api的形式控制,没有用websocket是因为涉及到第三方等其他因素,相当于是用了workerman全家桶了
方案一:@xiuwang提出的
$client = stream_socket_client('udp://ip:port'); stream_socket_sendto($client, 'udp数据');
,,测试过,这种方案客户端无法收到数据,但现实发送成功
方案二:socket_sendto 方案也测试,客户端也没有办法收到数据
方案三:建立一个异步UDP客户端 [AsyncUdpConnection]、测试过,客户端也无法收到数据
方案四:引入swoole里面的协程UDP客户端,然后指定ip和port发送消息,客户端也没有收到
以上方案不管适不适用,先当死马医,试了一遍,发现都不行,也可能是我方法不对,请教一下论坛各位大佬,有没有好的方案或者思路,希望不吝赐教。
最后的话:
workerman是真NB,之前做过一个TCP长连接项目,目前已经支持几万的设备了,毫无压力,这次是另外的项目,但可惜的是不是TCP而是UDP,不过这样也好,再次把workerman学习一遍。
用前也对比了一下swoole以及它孪生的hyperf、imi等,亲测后不管是开发成本还是对服务器的性能方面,还是workerman胜一筹,walkerNB。
不知道我理解的对不对哈
api发送数据给设备 然后redis存储标识 udp服务端接收数据 根据redis标识去匹配 然后存入异步队列
异步队列 将数据推送给websocket客户
谢答。现在我的问题不是发送消息给websocket客户,而是怎么在onMessage之外通过udp给客户发消息,IP和port都已经存储,现在是怎么发数据给设备的问题。
gatewayclinet 不可以么? 没搞过udp的 只搞过tcp的
GatewatWorker我之前做过项目很熟悉,只是这次的项目的硬件指定用UDP,没有办法。
无能为力, 坐等大佬回答 学习下
感谢,共同进步!
方案三:建立一个异步UDP客户端 [AsyncUdpConnection]、测试过,客户端也无法收到数据 先推给服务端,让服务端转发也不行?
这个貌似理论上没问题,我在onMessage里面保存$connection,然后收到用户侧的udp,再在onMessage里面去发送,稍等,我马上测试一下
成了,兄弟。
在api接口里用AsyncUdpConnection连接udp服务器,然后发送命令,udp服务收到消息后区分api发送的数据还是设备发送的数据,然后按照api的消息再下发命令,直接搞定、
那就好, 主要是我没弄过udp的, 不了解
兄弟再问一下,有没有全局保存$connection的方案,我想在onMessage里面把$connection保存下来,然后在webman里面使用
保存的话保存到全局变量就行了。
global $udpConnections;
$udpConnections[$connection->id] = $connection;
不要无限保存,长时间不用的$connection对象要close并且删除保存,否则内存里积攒的无用$connection对象越来越多,内存会上涨。
@静默 $udpConnections[$connection->id] = $connection; 这个不行
$udpConnections[$connection->getRemoteAddress()] = $connection; 这样
写一个函数
存connection的时候用 connection('连接标识', $connection);
取connection的时候用 connection('连接标识');
这个只能在单进程中使用
业务处理如果都放在了队列里,感觉udp只用一个进程就能支持几万设备使用了。
进程间这种资源类型的connection对象是共享不了的,多进udp程通讯也得像我那样存connection,然后再配合webman的channel插件。
不过udp最好是单进程用,因为一个客户端发多个udp消息给服务端,这多个udp消息可能会被服务端的不同的进程收到,这会导致多个进程都有生成了$connection对象,但是多个$connection对象对应的客户端都是一个。如果你用channel分布式通讯,多个进程都存储了对应客户端的$connection对象,都推送数据的话,客户端收到的数据会是重复的。
不是,我上面的意思是:上卖弄的connection方法只能在workerman里面的udp进程里面使用,但是现在我想在webman里面(webman里面用workerman创建了udp服务)调用udp进程里面保存的$connection,这样的话上面的connection方法貌似就不行了
这个 connection 你拿到 workerman 外部是使用不了的, 服务端 拿到connection 去转发 命令
跨进程不能共享$connection。你可以udp服务内部再监听一个udp端口,用于内部调用。webman通过udp给这个内部端口发数据,内部端口收到数据后再调用$connection('标识')->send(); 就能给客户端发了
假设webman里要给udp key为123的设备发数据,只需要调用
整体逻辑应该就是这样
@remix789 好的,非常感谢
@six 非常非常感谢,经过我这愚笨的大脑CPU高速运转,终于明白了第一段代码的意思,这个方法确实不错,非常感谢。
$client = stream_socket_client('udp://127.0.0.1:7070');
stream_socket_sendto($client, '123');
上面这段代码的意思我也明白了,问一个不相关的问题,stream_socket_client里面的IP地址可以直接填客户端udp的ip地址吗?然后通过stream_socket_sendto直接发消息给客户端的udp
如果调用stream_socket_client的服务器和udp服务器是在一个内网,应该可行
你保存ip和port是没有用的,你的客户端没有实现服务端的功能,用ip+port没有办法发送成功的;服务端之所以可以给客户端发消息,是因为使用了客户端主动和服务端建立的连接,当然udp不存在建立连接的说法,但大致意思是一样的。
谢答,那我这个有什么解决方案吗?
你可以参考webman/push的实现方式,也就是同一个服务监听两种协议和端口,监听udp的服务实现监听一个http,然后实现一个消息发布的接口,这个接口利用服务端保存的udp connection对udp客户端推送消息
https://github.com/webman-php/push/blob/main/src/Server.php 132-138
这里new了一个新的worker,并监听;分别实现了监听websocket和http,同理,你可以分别监听udp和http或者其他;
他们本质上在一个进程内,所以你可以拿到udp的connection对象,进行发布
本质上一个worker进程可以同时监听N个不同的协议,他们都会放在一个event-loop进行监听并回调执行触发,只要你分别保存各自协议的connection,就可以达到分别触达;但非必要不建议“集大成”,最好还是各自管理。
同一个局域网内客户端监听一个UDP端口,这样知道ip和port就能发,如果不在一个局域网那就要用p2p穿透了。