在手机卡牌游戏中使用http短链接如何实现推送消息比如ios 即时聊天消息推送系统礼包等?

&&&&&&&&&& HTTP本质是由客户端发起请求获取数据,要实现服务端推送消息,最简单的方式是利用ajax不断的轮询,间隔设置越小实时性越高,同时对服务器的压力越大。如果服务端产生消息的间隔比轮询间隔大很多的话,很多请求都是落空的,感觉有点浪费。自然想到使用长轮询的方式改进,请求到达服务端,当有消息的时候立即返回,并再发起一个请求,如果没有消息,则保持连接一段时间,比如30秒,然后重新发起请求。&这样请求的间隔根据消息产生的频率有个最大30秒弹性的间隔,并且能保证消息的实时推送。从fiddler抓包来看,每个客户端一直有个http连接和服务端处于连接状态,(ps:在网上看长连接和长轮询,iframe方式ajax方式,一直搞不清区别,我都认为一致了)。
&&&&&&&&&要实现账号重复登录踢出原先登录的账号,则需要定向推送消息,如何找到原先登录的客户端?需要找一个客户端连接唯一的标识,比如sessionid,每个客户端登录后,注册到已登录map中,并且对应生成一个消息队列。&后来登录的账户只要在已登录map中找到sessionid,然后往sessionid对应的消息队列推送消息,每个客户端保持的长连接实时从自己对应的消息队列中poll消息,然后执行相应的操作就行了。
&&&&&&&&流程图:
&&&&&&&& 代码:
public class LoginServlet extends HttpServlet{
public void init() throws ServletException {
// TODO Auto-generated method stub
ServletContext context = this.getServletContext();
// 如果是多个应用服务负载均衡,loginMap和eventMap和可以存储到分布式存储中或集中式数据库
context.setAttribute(&loginMap&, new ConcurrentHashMap&String, HttpSession&());
context.setAttribute(&eventMap&, new ConcurrentHashMap&String, ArrayBlockingQueue&Event&&());
public void service(ServletRequest request, ServletResponse response)
throws ServletException, IOException {
// TODO Auto-generated method stub
HttpServletRequest req = (HttpServletRequest)
HttpServletResponse res = (HttpServletResponse)
String name = req.getParameter(&usrName&);
String pwd = req.getParameter(&usrPwd&);
if(login(name,pwd)){
HttpSession session = req.getSession(true);
ConcurrentHashMap&String, HttpSession& loginMap = (ConcurrentHashMap&String, HttpSession&)this.getServletContext().getAttribute(&loginMap&);
ConcurrentHashMap&String,ArrayBlockingQueue&Event&& eventMap = (ConcurrentHashMap&String,ArrayBlockingQueue&Event&&)this.getServletContext().getAttribute(&eventMap&);
//该用户已登录
//multi thread not safe
if(loginMap.containsKey(name)){
HttpSession oginalSession = loginMap.get(name);
ArrayBlockingQueue&Event& events = eventMap.get(oginalSession.getId());
if(events==null){
events = new ArrayBlockingQueue&Event&(100);
Event event = new Event(1,&kicked&);
events.put(event);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
eventMap.put(oginalSession.getId(), events);
session.setAttribute(&user&, name);
loginMap.put(name, session);
eventMap.put(session.getId(),new ArrayBlockingQueue&Event&(100));
//登录成功
res.sendRedirect(&main.jsp&);
private boolean login(String name, String pwd){
轮询poll消息:
* @author husan
* @description:message push,like the login invadite message
public class MessagePushServlet extends HttpServlet{
private int waitTime = 30;
protected void doPost(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
// TODO Auto-generated method stub
ServletContext context = this.getServletContext();
HttpSession session = req.getSession();
ArrayBlockingQueue&Event& eventQueue = (ArrayBlockingQueue)((ConcurrentHashMap)context.getAttribute(&eventMap&)).get(session.getId());
if(eventQueue != null){
Event event = eventQueue.poll(waitTime, TimeUnit.SECONDS);
if(event == null){
//超时消息
event = new Event(-1,&time out&);
if(event.getFalg() == 1){
session.invalidate();
String ret = toJson(event);
resp.setContentType(&text/charset=UTF-8&);
resp.getWriter().println(ret);
resp.flushBuffer();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
private String toJson(Event event){
StringBuffer sb = new StringBuffer(&{\&flag\&:\&&);
sb.append(event.getFalg()).append(&\&,\&desc\&:\&&).append(event.getDesc())
.append(&\&}&);
System.out.println(sb.toString());
return sb.toString();
public int getWaitTime() {
return waitT
public void setWaitTime(int waitTime) {
this.waitTime = waitT
前端页面:&
&%@ page language=&java& contentType=&text/ charset=UTF-8&
pageEncoding=&UTF-8&%&
&!DOCTYPE html PUBLIC &-//W3C//DTD HTML 4.01 Transitional//EN& &http://www.w3.org/TR/html4/loose.dtd&&
&meta http-equiv=&Content-Type& content=&text/ charset=UTF-8&&
&title&main head&/title&
欢迎你: &%=session.getAttribute(&user&) %&
&script type=&text/javascript& src=&js/jquery-1.10.2.min.js& &&/script&
&script type=&text/javascript&&
function getMessage(){
$.ajax({url:&messagePoll&,
type:&post&,
dataType:&json&,
success:function(data){
if(!process(data)){
setTimeout(getMessage());
function process(data){
if(data.flag == 1){
//被踢下线,不再向服务端poll消息
if (confirm(&你已经被t下线!&)){
window.location.href = &login.html&;
$(function(){
getMessage();
&&&&&&&&&&&上面的例子只是个人学习的一个例子,并没有实际应用,没有考虑线程安全和用户数过多的情况。如果有很多的用户,或者起了多个应用负载均衡,那肯定不能使用ServletContext来保存数据,一般放到数据库或者各应用共享的分布式缓存中。如果消息很多的话,可不可以一次请求取出多条消息。。等等很多没有考虑。服务端处理连接的Connector使用nio多路就绪选择,参考下/blog//200-long-connection.html HTTP长连接200万尝试及调优。
参考知识库
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
访问:3212次
排名:千里之外

参考资料

 

随机推荐