xiaolingzi's blog

每天都在成长...

欢迎您:亲

基于Swoole实现的即时聊天通讯Socket服务(支持websocket连接)

xiaolingzi 发表于 2017-03-14 16:14:38

一、关于Swoole

关于Swoole的一切,包括环境搭建、接口说明、示例等文档,可以访问官方网站 http://www.swoole.com 。

所以swoole和php的环境搭建这里就不再啰嗦。

二、服务架构

1. 服务架构如下面的结构图:

服务端主要有http服务和socket服务,存储方式有mysql(持久化)和文件(或者内存如redis)。http服务主要用来提交要发送的消息存到数据,成功之后才通过socket进行消息推送。http提交信息比较简单也不是要讲的重点,这里也不多说。用户在连接上服务器之后,服务器需要为当前连接与对应用户的映射关系和群组在线用户消息,这些数据可以通过文件或者内存的方式进行缓存。

1. 连接池,看了swoole的教程我们知道,每个连接都会有一个唯一的id,这里我们称之为fd。由于我们发送消息时不可能每次都带上自个的用户消息,这样会很浪费带宽,所以我们就要将fd作为索引建立连接池,建立当前fd和对用用户id的映射关系,这样我们就可以通过fd快速检索用户信息,这里我们使用swoole table来存储。

2. 待检池。每个连接都必须在规定时间内通过身份验证才能保持连接,否则连接被关闭。待检池就是用来存储新连接用于检查他们的合法性,检查过程中发现验证通过或者连接超时都从待检池移除,并对超时连接进行关闭。这些数据使用swoole table来存储。

3. 群组通知池。由于swoole table的局限性(当然可以使用redis,为了简单这里使用文件),该部分数据使用文件进行存储,该目录存储的是每个群组的在线用户,记录的是在线用户的fd,当我们收到该群的消息时,我们就可以通过群id获取在线的连接并广播群消息。

3. 用户数据。该目录使用用户id为索引缓存用户信息,同时建立用户id和对应连接fd的映射关系,这样我们就可以通过用户id快速检索对应的连接fd来发送数据。 我们知道,在给另外一个用户发消息的时候我们往往只知道对方的用户id,而只知道用户id无法知道将消息推送给哪个socket连接,有了用户id和fd的映射,就可以解决该问题。


2. 存储文件部分文件结构如下:


三、业务流程

1. 双人聊天业务流程图如下:

2. 群聊业务流程图如下:

为了同时支持socket和websocket连接,每次连接都先判断连接类型,对于websocket要进行简单的握手。关于websocket的讲解可以看这篇以下文章:

https://www.xxling.com/blog/article/3103.aspx

更多的逻辑请直接查看代码。


五、消息格式

客户端消息发送格式如下(注意要加上结束符#$#):

身份验证

{"infoType": 100001,"connectionType": 2,"data":{"token":"","userId":1}}#$#

实际应用中token改为实际验证的token

双人对话

{"infoType": 110001,"connectionType": 2,"data":{"userId":1,"toUserId":2,"messageContent":"user message","messageType":1}}#$#

群聊消息

{"infoType": 110002,"connectionType": 2,"data":{"userId":1,"clubId":1,"messageContent":"club message","messageType":1}}#$#

服务端返回消息格式:

双人对话

{"code":"N00000","message":"xxx","infoType":110001,"data":{"userId":1,"toUserId":2,"messageContent":"user message","messageType":1,"userName":"test1","avatar":""}}#$#

群聊

{"code":"N00000","message":"xxx","infoType":110002,"data":{"userId":1,"clubId":1,"messageContent":"club message","messageType":1,"userName":"test1","avatar":""}}#$#

消息主要使用json格式进行传输,传输的内容和格式大家可以自己定义,代码层面做相应修改就好,infoType的含义看项目下的 CommonDefine.php 文件,这里就不多说。


六、运行

在安装好swoole和php之后,直接运行项目文件夹下的App.php文件

php /xxx/application/Projects/IMSocket/App.php -i e

前台后台运行可以通过SwooleServer.php里面的配置项进行配置,还要注意将公共和项目下的config目录中的配置修改为自己的


七、测试

项目目录下有test1.html和test2.html两个文件,分别在两个浏览器中打开,就可以用来简单的测试收发数据了。


八、代码下载

application.rar

GitHub地址 https://github.com/xiaolingzi/Swoole.IMSocket


最后 代码里面有不少当时调试时的输出,可以根据自身情况删除掉。


      

转载请注明出处:http://www.xxling.com/article/3106.aspx

  • 分类: PHP
  • 阅读: (7541)
  • 评论: (8)
砖墙
波波
断线可以重连吗? 有没有心跳机制
xiaolingzi 波波
这个是服务端,断线重连客户端来做。服务端swoole本身就内置有心跳功能,主要是用来关闭长时间没有数据来往的连接。
flyphper
一直在研究怎么开发聊天室,看了这篇文章,解决了我的不少困惑,豁然开亮的感觉。
xiaolingzi flyphper
很高兴能帮到你,多交流
大大
博主,有个小问题,发送群聊信息的时候带上clubId,当前clubId的用户怎么确认fd? 是不是在服务端进行查库,确认当前clubId下的所有成员userid,然后轮询到当前连接用户userid进行比对确认fd,从而进行推送?
xiaolingzi 大大
服务端有维护每个俱乐部的在线成员列表(比如用户上线和下线时去更新他所在俱乐部的状态),在线成员信息里有包含fd,所以服务端通过clubId拿到在线用户的fd列表直接广播出去就可以了。
燎原
“同时支持socket和websocket连接”是怎么实现的,我用send()返回握手http协议报头,总是握手失败
xiaolingzi 燎原
可以通过websocket连接时发送的数据中的关键数据进行判断是否是websocket,比如“Sec-WebSocket-Key”
拍砖 取消
请输入昵称
请输入邮箱
*
 选择评论类型
300字以内  请输入评论内容