一、寫在前面
要求做一個,后台發布信息,前台能即時得到通知的消息推送功能。網上搜了也有很多方式,ajax的定時詢問,Comet方式,Server-Sent方式,以及websocket。表示除了定時詢問外,就websocket相對簡單點。
二、實現
實現類java代碼:
package cn.xm.mall.websocket.controller; import java.io.IOException; import java.nio.ByteBuffer; import java.nio.CharBuffer; import java.text.SimpleDateFormat; import java.util.Date; import java.util.HashMap; import java.util.Map; import java.util.Set; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServletRequest; import org.apache.catalina.websocket.MessageInbound; import org.apache.catalina.websocket.StreamInbound; import org.apache.catalina.websocket.WebSocketServlet; import org.apache.catalina.websocket.WsOutbound; @WebServlet("/webSocket.do") public class WebSocketServletController extends WebSocketServlet { private final Map<Integer, WsOutbound> map = new HashMap<Integer, WsOutbound>(); private static final long serialVersionUID = -1058445282919079067L; @Override protected StreamInbound createWebSocketInbound(String arg0, HttpServletRequest request) { // StreamInbound:基於流的WebSocket實現類(帶內流),應用程序應當擴展這個類並實現其抽象方法onBinaryData和onTextData。 return new ChatMessageInbound(); } class ChatMessageInbound extends MessageInbound { // MessageInbound:基於消息的WebSocket實現類(帶內消息),應用程序應當擴展這個類並實現其抽象方法onBinaryMessage和onTextMessage。 @Override protected void onOpen(WsOutbound outbound) { map.put(outbound.hashCode(), outbound); super.onOpen(outbound); } @Override protected void onClose(int status) { map.remove(getWsOutbound().hashCode()); super.onClose(status); } @Override protected void onBinaryMessage(ByteBuffer buffer) throws IOException { } @Override protected void onTextMessage(CharBuffer buffer) throws IOException { String msg = buffer.toString(); Date date = new Date(); SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss"); //msg = " <font color=green>匿名用戶 " + sdf.format(date) + "</font><br/> " + msg; broadcast(msg); } private void broadcast(String msg) { Set<Integer> set = map.keySet(); for (Integer integer : set) { WsOutbound outbound = map.get(integer); CharBuffer buffer = CharBuffer.wrap(msg); try { outbound.writeTextMessage(buffer); outbound.flush(); } catch (IOException e) { e.printStackTrace(); } } } } }
前台連接websocket 代碼 寫到了js里,頁面直接引用,就和websocket連通了。
/** * WebSocket消息推送 * @Copyright Copyright (c) 2006 * @author Guapo * @see DESCore */ $(function() { var loginNameCookie=$.cookie('memberInfo'); if(typeof(loginNameCookie) == "undefined" || loginNameCookie==null || loginNameCookie==''){}else{ chat(); } }); var socket; var chat = function() { socket = new WebSocket('ws://XXXXX/webSocket.do'); socket.onopen = function(event) { console.info("<font color=green>連接成功!</font>"); }; socket.onmessage = function(event) { if("pms"==event.data){ appendInsideLetter();//這里是后台send一個pms參數類型,前台得知后,異步查詢后台通知的資訊,然后頁面通知動作的方法 } }; socket.onclose = function(event) { console.info("<font color=green>連接斷開!</font>"); }; if (socket == null) { console.info("<font color=green>連接失敗!</font>"); } }; var send = function() {
后台同理和前台連接同一個websoket后。在發布完信息后,調用一下send方法。代碼如下
var socket; var chat = function() { socket = new WebSocket('ws://www.simaakj.com/webSocket.do'); socket.onopen = function(event) { console.info("<font color=green>連接成功!</font>"); send();//后台連接的時候,就通知了一下。 }; socket.onmessage = function(event) { }; socket.onclose = function(event) { console.info("<font color=green>連接斷開!</font>"); }; if (socket == null) { console.info("<font color=green>連接失敗!</font>"); } }; var send = function() { socket.send("pms"); }
后台操作完,調用 chat();既可推送告知前台成功。
三、遇到的問題,總結
0、The hierarchy of the type MyMessageInbound is inconsistent。一開始遇到這個問題,原因是缺少包,不僅僅需要tomcat中的catalina.jar以及websocket-api.jar。還需要tomcat-coyote-7.0.27.jar
1、以上代碼放到本地后,開發發現連不上,發現需要在登錄過濾把webSocket.do去掉,去掉之后,本地運行好使了。
2、放到環境中后,發現又有問題,原來是環境用到了Nginx,導致websoket不好使,百度了下解決辦法,在Nginx配置中加入以下代碼,就好使了。
我是在reverse-proxy.conf中添加的
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";