欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  后端开发

关于socket开发的一些疑问

程序员文章站 2024-01-19 11:41:28
...
最近要筹备做一个类似小型游戏服务端的东西,我玩过一下WebSocket,用手机做成PPT遥控器一样控制WEB幻灯片上翻下翻播放然后在会议上一边讲解我的东西,但并不深入,还有一些疑问想找大家讨教一下:

1.损耗问题:据我学习到的,php在socket这方面是启动一个php脚本,这个脚本通过socket的api监听本机的特定端口,然后不像web请求那样一瞬即逝,而是几乎死循环地不断重复运行重复监听端口,类似我找到的例子代码是用while(true)的逻辑来实现持续监听而不让脚本执行到尾部就销毁线程,那这一秒内能while(true)多少次,监听多少次,这个效率真的没问题吗?看上去也只能这样死循环了,变相一下实现死循环估计也差不多的吧,只是频率方面会不会太耗CPU呀,初步认为是不会的,其实如果加sleep的话就算睡眠1秒,那响应也有延迟用户体验肯定不好,所以高频率监听估计也是必须的,经验者的总结是?


2.进程控制问题:先不说什么大并发,咱小项目几年里也不愁这个,,就是给用户一个实时的功能而已,我在服务端通过 php server.php 命令启动了这个server脚本,它就一直在监听了,可是我要关闭服务器时咋办?不可能是 php server.php -k stop 啥的吧,这样会触发一个新的线程吧?咋办呢,如果我要维护升级或迁移,怎么让它停止运行呢???
查找进程,找出PID再kill掉吗,可是,如果有其它http的请求触发了web目录下的index.php啥的,那也会是php的进程吧,那如果找出一堆php的进程后怎么识别哪种是server.php这个脚本的pid?好吧笨方法就全部kill掉好了...呵呵,别笑我,其实我也不想的,因为我还设想过,通过生成一个 close文件,值为1来实现,server.php每次监听都会先打开这个文件看看值是不是0,不是就exit...可是这个IO损耗绝对是不理智的吧!我再想想我再想想....我想不出来了,尽是些笨方法,大神们…………


3.内存问题:多个socket客户端连接到服务端后,服务端都是用之前那个server.php的进程来处理的吧,并没有启动新的进程,只是原来的进程监听到了数据做响应,那么如果我的监听函数里弄个数组,来一个人我就追加一个IP,那来好多个人后就会有好多个IP,慢慢地运行久了,估计上几十几百万IP不成问题了,当然断开的时候删除数组元素是要的吧.这只是打个比方,我是想说这么看来所有客户端的处理处于一个进程下,是共享内存的,能互相操作访问,比如A用户在某些条件下要导致B用户的HP减少,或者金钱减少(交易功能这些),于是,当用户多起来的时候,想存这些东西可不能用变量了,都得找缓存了,对不对?


4.规划问题:需求有点那个哦,比如这是个实时聊天室吧,我们网站本来是个普通网站一样,到处是普通的HTTP请求返回的HTML代码呈现的页面,可是这个功能就像那些 53客服 插件一样,是实时通讯的东西,几乎开每个页面都要有这个东西,那每次加载都要连接服务端,有时候用户是新窗口打开的,旧窗口又不关,就会有两个连接了吧,前端方面,这个不知能不能通过什么来节省一下资源,好像53客服那些你不点它就不会连接的吧,可是我们的需求不同,它有消息提醒功能,那用户进入新页面后,如果有其他用户发起交易请求,那么这个小栏目也得红灯闪闪地叫喊着“有新交易啦!”,这意味着客户端就需要监听才能收到消息了吧?那就是每次进页面就进开始连接了,要不转下弯,这个监听消息的另外连接一个服务端的端口,这个服务端可以分离部署在一台专用的消息服务器上,前端收到消息后,点击弹出一个框才连接主要即时业务的那个服务端,这里增加了服务端架构的复杂度,但负载损耗上估计是会好一点,我就懂这点小伎俩而已了,更多的分布式暂时不能考虑,只是为了给我们的少部分用户一个比较好的体验


5.监控问题:程序逻辑不一定是非常完美的,那么可能会出错and抛异常,那一旦出错这个php脚本的进程就会没了吧,那其它新的客户端连接就会连接失败了,还要做一个持续监听的东西来监控是否挂掉,挂了就exec('php server.php') ?这样吗?按照工作以来的认知,应该是用守护进程来搞的吧,就是用php做一个守护进程的脚本,由它来exec调用server.php派生服务端线程,yes?可是守护进程又怎么了解到之前的线程挂没挂呢,是不是都会自动生成一个pid的文件在某个目录下?好像别的软件都能指定生成在自己的目录下.也许能通过某种方式区得进程号自己file_put_contents吧


希望大家分享一下以上问题的意见,谢谢!


回复讨论(解决方案)

php 没有提供 WebSocket 服务器功能,所以自己写的话只能用循环等待的方式。显然空转也是要消耗资源的
如果你的服务器是 Linux 系统,那么可以在网上找到 php 结合系统功能的 WebSocket 服务器。开源的,据说性能还不错
如果你的服务器是 windows 的,那么最好不要用纯 php 代码去做,不然日后会遇到很多麻烦,不是 php 层面可以解决的

所以如果真的要自己写 WebSocket 服务器的话,建议你使用 node.js,至少不需要再学一门语言
使用 node.js 写 WebSocket 服务器,实现基本功能只需 4~5 行代码

其他问题你基本上都考虑到了,到实战时逐一解决吧

前面看到一个200W下用户免费的一个第三方IM,它是用的长连接。
你用PHP socket来做,全部靠PHP可能不太现实,配合着系统脚本来做呢。。。
我也没去真正研究过。。。

可以直接用这个,性能很强悍,pc四核长链接qps为15W/S,单服务器轻松支撑几万客户端
主页: http://www.workerman.net/,上面有很多websocket的例子,包括游戏
源码: https://github.com/walkor/workerman

针对你的问题回答如下:

1\ 它是非阻塞+IO复用(Epoll),服务器模型类似nginx,性能非常好
2\ 自带进程控制,php your_file.php start -d 自动进入daemon模式, php your_file.php stop 停止,php your_file.php status 查看状态
3\ 支持变量或者资源永久保持,变量放到内存中,当然也可以放到mysql redis等存储里面
4\ 可以独立与原有web系统单独部署,它的GatewayWorker模型天然支持分布式部署
5\ 如果子进程挂掉可以自动拉起,通过status能看到进程是否挂掉、挂掉几次。它也有statistics监控应用,可以监控各个业务接口成功率、调用曲线、耗时等等

附上websocket服务端demo:

count = 4;// Emitted when new connection come$ws_worker->onConnect = function($connection){    // Emitted when websocket handshake done    $connection->onWebSocketConnect = function($connection)    {        echo "New connection\n";    };};// Emitted when data received$ws_worker->onMessage = function($connection, $data){    // Send hello $data    $connection->send('hello ' . $data);};// Emitted when connection closed$ws_worker->onClose = function($connection){    echo "Connection closed\n";};// Run workerWorker::runAll();

换个GO 玩玩....

swoole或者workerman 如果纯使用不研究源码。推荐swoole否则workerman

可以直接用这个,性能很强悍,pc四核长链接qps为15W/S,单服务器轻松支撑几万客户端
主页: http://www.workerman.net/,上面有很多websocket的例子,包括游戏
源码: https://github.com/walkor/workerman

针对你的问题回答如下:

1\ 它是非阻塞+IO复用(Epoll),服务器模型类似nginx,性能非常好
2\ 自带进程控制,php your_file.php start -d 自动进入daemon模式, php your_file.php stop 停止,php your_file.php status 查看状态
3\ 支持变量或者资源永久保持,变量放到内存中,当然也可以放到mysql redis等存储里面
4\ 可以独立与原有web系统单独部署,它的GatewayWorker模型天然支持分布式部署
5\ 如果子进程挂掉可以自动拉起,通过status能看到进程是否挂掉、挂掉几次。它也有statistics监控应用,可以监控各个业务接口成功率、调用曲线、耗时等等

附上websocket服务端demo:

count = 4;// Emitted when new connection come$ws_worker->onConnect = function($connection){    // Emitted when websocket handshake done    $connection->onWebSocketConnect = function($connection)    {        echo "New connection\n";    };};// Emitted when data received$ws_worker->onMessage = function($connection, $data){    // Send hello $data    $connection->send('hello ' . $data);};// Emitted when connection closed$ws_worker->onClose = function($connection){    echo "Connection closed\n";};// Run workerWorker::runAll();

看上去不错,我尝试一下,谢谢!