以前做项目的时候常常会有客户端提醒的功能,使用的是ajax长轮询的方式,这中方式对服务器端压力比较大,无论有没有通知都会发送心跳请求,最近看了一下html5以及tomcat对websocket支持的相关文章,自己做了个小东东,仅供大家分享.
demo实现的小功能: 客户端A发送请求,通过服务器,直达客户端B
演示效果如图所示
代码的目录结构
package com.bw.websocket.api; import java.io.IOException; import java.util.HashMap; import java.util.Map; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.ResponseBody; import com.bw.websocket.servlet.CallCenterMessageInBound; @Controller public class MessageAPI { /** * 向客户端推送消息 * */ @ResponseBody @RequestMapping(value = "/api/send.action") public Map<String, String> send(@RequestParam String clientId, @RequestParam String message) { Map<String, String> result = new HashMap<String, String>(); try { CallCenterMessageInBound.send(clientId, message); result.put("message", "success"); } catch (IOException e) { result.put("message", e.getLocalizedMessage()); e.printStackTrace(); } return result; } }
package com.bw.websocket.servlet; import java.io.IOException; import java.nio.ByteBuffer; import java.nio.CharBuffer; import java.util.HashMap; import java.util.Map; import org.apache.catalina.websocket.MessageInbound; import org.apache.catalina.websocket.WsOutbound; public class CallCenterMessageInBound extends MessageInbound { private String clientId; public static final Map<String, MessageInbound> socketList = new HashMap<String, MessageInbound>(); public static void send(String clientId, String message) throws IOException { MessageInbound messageInbound = socketList.get(clientId); messageInbound.getWsOutbound().writeTextMessage( CharBuffer.wrap(message)); messageInbound.getWsOutbound().flush(); } public CallCenterMessageInBound(String clientId) { this.clientId = clientId; } @Override protected void onBinaryMessage(ByteBuffer message) throws IOException { } @Override protected void onTextMessage(CharBuffer message) throws IOException { } @Override protected void onOpen(WsOutbound outbound) { socketList.put(clientId, this); } @Override protected void onClose(int status) { socketList.remove(clientId); } }
package com.bw.websocket.servlet; import java.io.IOException; import java.nio.ByteBuffer; import java.nio.CharBuffer; import java.util.HashMap; import java.util.Map; import org.apache.catalina.websocket.MessageInbound; import org.apache.catalina.websocket.WsOutbound; public class CallCenterMessageInBound extends MessageInbound { private String clientId; public static final Map<String, MessageInbound> socketList = new HashMap<String, MessageInbound>(); public static void send(String clientId, String message) throws IOException { MessageInbound messageInbound = socketList.get(clientId); messageInbound.getWsOutbound().writeTextMessage( CharBuffer.wrap(message)); messageInbound.getWsOutbound().flush(); } public CallCenterMessageInBound(String clientId) { this.clientId = clientId; } @Override protected void onBinaryMessage(ByteBuffer message) throws IOException { } @Override protected void onTextMessage(CharBuffer message) throws IOException { } @Override protected void onOpen(WsOutbound outbound) { socketList.put(clientId, this); } @Override protected void onClose(int status) { socketList.remove(clientId); } }
<%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="utf-8"%> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8"> <title>Insert title here</title> <script type="text/javascript" src="js/jquery-2.0.2.min.js"></script> <script type="text/javascript"> var clientId =Math.random(); var url = 'ws://localhost:8080/websocket/WebSocket?clientId='+clientId; document.write("Current ClientId:"+clientId); var CallCenter = { init:function(url){ var _websocket = new WebSocket(url); _websocket.onopen = function(evt) { console.log("Connected to WebSocket server."); }; _websocket.onclose = function(evt) { console.log("Disconnected"); }; _websocket.onmessage = function(evt) { eval(evt.data); }; _websocket.onerror = function(evt) { console.log('Error occured: ' + evt); }; } }; CallCenter.init(url); $(document).ready(function(){ $("#send").bind("click",function(){ $.ajax({ type: "get", url: "api/send.action", data: "clientId="+$("#clientId").val()+"&message="+$("#message").val(), success: function(msg){ alert(msg.message); } }); }); }); </script> </head> <body> <br/> To ClientId: <input type="text" id="clientId" /> <br /> Message: <input type="text" id="message" /> <input type="button" id="send" value="send"> </body> </html>
home.jsp 监控客户端连接
<%@page import="com.bw.websocket.servlet.CallCenterMessageInBound"%> <%@page import="java.util.Iterator"%> <%@page import="org.apache.catalina.websocket.MessageInbound"%> <%@ page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1"%> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"> <title>Insert title here</title> </head> <body> <% for(Iterator<String> it = CallCenterMessageInBound.socketList.keySet().iterator();it.hasNext();) { String key = it.next(); out.println(key+":"+CallCenterMessageInBound.socketList.get(key)+"<br/>"); } %> this home page </body> </html>
上述方法只对高版本的浏览器使用,也就是支持websocket的浏览器适用
考虑到浏览器的兼容性问题,我们还可以使用flash socket的方式进行消息推送,通过flex javascript bridge来执行服务器端推送信息.