WebSocket是一种在单个TCP连接上进行全双工通讯的协议。和使用ajax之类http请求所不同的是,Websocket允许服务端主动向客户端推送数据,同时也避免了使用ajax轮询造成的服务器资源浪费或者长连接带来的性能问题。也就是这个特性,使得它在很多场景得到应用,最典型的就是聊天功能。
使用原生PHP写socket比较繁琐,所以这里用workerman框架进行 PHP socket 的开发。
workerman开发手册
workerman的Github传送门
基本的使用自行查看workerman手册,这里主要讲一下我个人开发过程的思路。
由于是常驻内存的运行机制,无法像之前一样实时读写session;
并且是由于非HTTP协议的应用,与HTTP协议相关的变量函数(包括session)都无法直接使用。
虽然workerman框架提供了封装在Workerman\Protocols\Http类里面替代的HTTP函数,但是在非http协议的应用中是用不了session和cookie的。
在这里我是使用的redis去实现对当前用户是否真正登录的验证,从而决定是否允许其继续保持websocket连接。(不过这一步我做的不是很严谨,有兴趣可以看代码找漏洞)
一个用户可能在浏览器上同时开了多个页面,而websocket是无法对多个浏览器页面保持同一个连接的(这是不现实的),所以需要存储一个用户所同时保持的websocket连接(用connect_id标识)
当一个用户通过websocket发送消息时,附带上接收者ID,然后服务器通过redis找到接收者所保持的所有websocket连接(用redis set存储),进而将消息发送给这几个连接:
同样的,一个用户在一个页面发送了消息,他打开的其他页面也要同步显示刚发送的消息,用同样的方法实现。
其实我一开始是用的MeepoPS框架(也是一个PHP socket的框架),后来部署到服务器上发现实现了全站https的缘故,无法正常使用ws协议;而MeepoPS又不支持wss协议(websocket的基于SSL的安全传输协议),因而马上转手workerman。
使用workerman实现wss协议非常简单,详见 创建wss服务 - workerman手册
在这之前,首先要有SSL证书(pem/crt文件及key文件),可参考上一篇博客 Linux Server + Nginx 配置HTTPS
不啰嗦,贴代码
以及,前端代码要改为:
ws = new WebSocket("wss://myafei.cn:19910"); //使用域名+端口号
在这个网站注册登录后,可通过导航栏右边 Hello, {YourUserName}
处点击进入个人中心,你会看到和我的聊天窗口的(笑
PC端
手机端
整个网站的源代码,放在Github上了:传送门