概述
本文档就http长连接实现聊天室功能作一个说明,主要覆盖核心的功能点,关于细节方面的实现这里不再概述。
此版本的demo,后台用java实现,前端用python实现。
设计理念
comet长连接有两种方式:长轮询方式(long-polling)和流方式(streaming)。
长轮询方式:HTTP的连接保持,服务端会阻塞请求,直到服务端有事件触发 OR 超时。客户端在收到相应后,再次发送请求建立连接。
流方式:服务器推送数据给客户端,但是不关闭连接,保持连接,直至超时。超时后客户端关闭连接,同时重新建立连接。Java 通过它的NIO库提供非阻塞IO处理Comet。
设计流程
短连接和长连接区别
短连接是tcp connection —> http get/post —> tcp close
长连接是tcp connection —> http get/post —> http get/post —> http get/post —> tcp close
客户端
1、设置‘Connection’ : ‘Keep-alive’ 请求头。
2、循环访问服务器,去服务器拿聊天信息。根据个人发信息的频率,间隔时间设置在2~3秒为益。也可以不用设置间隔时间,每时每刻去服务器拿数据。个人偏向前者,可以减少程序负担和网络负担。
3、如果用户触发发送聊天消息,可以单独启用一个程序进行发送消息(短连接)。
也可以复用上面步骤2中的连接。个人偏向后者,可以减少多余的tcp连接,可以 tcp连接复用。
服务端
1、在服务器端开启keep-alive,配置服务器的连接超时时间。
2、设置一个静态类Group,类中定义一个静态HashMap成员变量,key表示房间,存储现场id。value表示该房间里面有哪些成员,存储一个user对象。
3、设置一个User类,用于存储某个房间下有哪些成员。定义一个成员变量HashMap,key用于存储用户id,value为一个list,存储该用户当前的消息队列。
4、服务器在启动的时候,会根据数据库中记录的信息,初始化房间和房间里面的人员。
5、如果一个新用户加入聊天室,则向User类中HashMap成员对象中插入一条key和value的值即可,同时这条记录要写入数据库。
如果一个用户退出群,删除User类中HashMap的值即可,同时删除数据库中的记录信息。
6、如果一个用户向大家发送群聊信息,服务端得到该用户所在的现场组id即房间id和用户id。然后在Group对象中,取出该房间id对应的User对象。依次遍历User对象中成员,将该用户发的信息放进每个用户的消息队列。
如果一个用户向某个用户发送私信。服务器取到接送放的id,然后将信息存储在接收方的消息队列中即可。
设计注意点:
1、多个线程操作同一个类或者对象的时候,这里特别要注意采用线程安全的思路。
2、用户推送的消息相当于存储在服务器的内存中,当服务器异常或者重启,将导致内存存储的用户消息丢失。如果后期对聊天数据是否丢失很敏感的话,考虑存储在数据库中。
3、以上代码设计,会发现一个问题,就是当大量用户进来取自己的聊天数据的时候,根据以上的设计,用户线程会首先争取房间类中HashMap成员变量的锁,这样导致其他用户线程等待。所以面对大用户量,以上设计方案不是最好,在此说明一下。
客户端和服务端实现总结
客户端(python版本)代码示例:
http长连接设置时的误区一:
第18行代码,每次循环,都会建立新的http连接,导致虽然设置了connection:Keep-Alive,但每次会新的连接请求,达不到连接复用。
正确的编码如下:
服务端(设置keep-alive超时时间),tomcat示例:
KeepAlive的连接活跃时然是受KeepAliveTimeOut限制的,如果第二次请求和第一次请求之间超过KeepAliveTimeOut的时间的话,第一次连接就会中断,在新建第二个连接。
客户端2~3秒请求一次,服务器端采用默认值5s。
验证测试
1、首先安装Wireshark抓包工具,用户我们测试验证用。下载地址如下:
http://wiresharkdownloads.riverbed.com/wireshark/win64/Wireshark-win64-1.10.2.exe
2、先来看看http短连接请求是怎么回事(将http头设置connection:close),我们用Wireshark抓包来看看,结果如下:
3、http长连接请求(将http头设置connection:keep-alive), Wireshark抓包结果如下: