tomcat8才支持 websocket
用到了 websocket-api.jar 包
package com.appwx.test; import java.io.IOException; import java.util.HashMap; import java.util.Hashtable; import java.util.Map; import java.util.concurrent.CopyOnWriteArraySet; import java.util.concurrent.atomic.AtomicInteger; import javax.websocket.OnClose; import javax.websocket.OnMessage; import javax.websocket.OnOpen; import javax.websocket.Session; import javax.websocket.server.ServerEndpoint; import com.jfinal.kit.JsonKit; /** * @class MyWebSocket.java * @author tuozq * @date 2017年3月3日 下午1:05:05 * @descriptions */ //@ServerEndpoint 注解是一个类层次的注解,它的功能主要是将目前的类定义成一个websocket服务器端。注解的值将被用于监听用户连接的终端访问URL地址。 @ServerEndpoint("/mywebsocket") public class MyWebSocket { // 静态变量,用来记录当前在线连接数。应该把它设计成线程安全的。 private static final AtomicInteger onlineCount = new AtomicInteger(0); // concurrent包的线程安全Set,用来存放每个客户端对应的MyWebSocket对象。若要实现服务端与单一客户端通信的话,可以使用Map来存放,其中Key可以为用户标识 private static Hashtable<String, Session> sessionMap = new Hashtable<String, Session>(); private static CopyOnWriteArraySet<MyWebSocket> webSocketSet = new CopyOnWriteArraySet<MyWebSocket>(); //定义一个记录客户端的聊天昵称 private final String nickname; // 与某个客户端的连接会话,需要通过它来给客户端发送数据 private Session session; public MyWebSocket() { nickname = "访客"+onlineCount.getAndIncrement(); } //onMessage 方法被@OnMessage所注解。这个注解定义了当服务器接收到客户端发送的消息时所调用的方法。 //注意:这个方法可能包含一个javax.websocket.Session可选参数(在我们的例子里就是session参数)。如果有这个参数,容器将会把当前发送消息客户端的连接Session注入进去。 @OnMessage public void onMessage(String message, Session session) throws IOException, InterruptedException { // Print the client message for testing purposes System.out.println(this.session==session); Map<String, Object> retMap = new HashMap<String, Object>(); retMap.put("msg", message); retMap.put("nickname", nickname); broadcast(retMap); } //onOpen 和 onClose 方法分别被@OnOpen和@OnClose 所注解。这两个注解的作用不言自明:他们定义了当一个新用户连接和断开的时候所调用的方法。 @OnOpen public void onOpen(Session session) { this.session = session; sessionMap.put(this.nickname, this.session); webSocketSet.add(this); String message = String.format("[%s,%s]",nickname,"加入聊天室"); Map<String, Object> retMap = new HashMap<String, Object>(); retMap.put("msg", message); retMap.put("nickname", nickname); broadcast(retMap); System.out.println("onOpen"); } @OnClose public void onClose(Session session) { sessionMap.remove(this.nickname); webSocketSet.remove(this); String message = String.format("[%s,%s]",nickname,"离开了聊天室链接"); Map<String, Object> retMap = new HashMap<String, Object>(); retMap.put("msg", message); broadcast(message); } //完成群发 private void broadcast( Map<String, Object> retMap ){ for(MyWebSocket w:webSocketSet){ try { synchronized (MyWebSocket.class) { if(!w.session.getId().equals(this.session.getId())){ String jsonstr = JsonKit.toJson(retMap); System.out.println(jsonstr); w.session.getBasicRemote().sendText(jsonstr); } } } catch (IOException e) { System.out.println("向客户端"+w.nickname+"发送消息失败"); webSocketSet.remove(w); try { w.session.close(); } catch (IOException e1) {} String message = String.format("[%s,%s]",w.nickname,"已经断开链接"); Map<String, Object> _retMap = new HashMap<String, Object>(); _retMap.put("msg", message); broadcast(_retMap); } } } private void broadcast(String msg){ for(MyWebSocket w:webSocketSet){ try { synchronized (MyWebSocket.class) { if(!w.session.getId().equals(this.session.getId())){ w.session.getBasicRemote().sendText(msg); } } } catch (IOException e) { System.out.println("向客户端"+w.nickname+"发送消息失败"); webSocketSet.remove(w); try { w.session.close(); } catch (IOException e1) {} String message = String.format("[%s,%s]",w.nickname,"已经断开链接"); broadcast(message); } } } //对用户的消息可以做一些过滤请求,如屏蔽关键字等等。。。 public static String filter(String message){ if(message==null){ return null; } return message; } }
js:
<script type="text/javascript"> var webSocket = null; //判断当前浏览器是否支持WebSocket if ('WebSocket' in window) { webSocket = new WebSocket('ws://localhost:8080/mywebsocket'); } else { alert('当前浏览器 Not support websocket') } webSocket.onerror = function(event) { onError(event); }; webSocket.onopen = function(event) { onOpen(event); }; webSocket.onmessage = function(event) { onMessage(event, false); }; function addMsgInfo(message, iscurr) { var jsonObj = null; if(!iscurr){ jsonObj = JSON.parse(message.data); }else{ jsonObj = message.data; } var html = ""; html += '<div class = "msginfo">'; if (iscurr) { html += ' <p class = "msgcoontent"><span class = "floatRight">' + jsonObj.msg + '</span></p>'; } else { html += ' <p class = "msgtitle">' + jsonObj.nickname + '</p>'; html += ' <p class = "msgcoontent"><span>' + jsonObj.msg + '</span></p>'; } html += '</div>'; $("#msgWindow").append(html); } function onMessage(event, iscurr) { addMsgInfo(event, iscurr); } function onOpen(event) { //alert("连接成功!"); } function onError(event) { alert(event.data); } function sendmsg() { var sendmsg = $("#txtinputmsg").val(); if (sendmsg) { var msgdata = {data : {msg:sendmsg}}; addMsgInfo(msgdata, true); webSocket.send(sendmsg); $("#txtinputmsg").val(""); } } function clearmsg() { $("#msgWindow").html(""); } $(".friendsul li").click(function(){ $(".friendsul li").removeClass("active"); $(this).addClass("active"); }); </script>
jfinalConfig中要配置一下,对WebSocket的actionkey放行,否则会被jfinal的filter拦截
@Override public void configHandler(Handlers me) { // TODO Auto-generated method stub me.add(new WebSocketHandler("/mywebsocket")); }
package com.appwx.core.handler; import java.util.regex.Pattern; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import com.jfinal.handler.Handler; import com.jfinal.kit.StrKit; /** * @class WebSocketHandler.java * @author tuozq * @date 2017年3月3日 下午2:12:21 * @descriptions */ public class WebSocketHandler extends Handler { private Pattern filterUrlRegxPattern; public WebSocketHandler(String filterUrlRegx) { if (StrKit.isBlank(filterUrlRegx)) throw new IllegalArgumentException("The para filterUrlRegx can not be blank."); filterUrlRegxPattern = Pattern.compile(filterUrlRegx); } @Override public void handle(String paramString, HttpServletRequest paramHttpServletRequest, HttpServletResponse paramHttpServletResponse, boolean[] paramArrayOfBoolean) { // TODO Auto-generated method stub if (filterUrlRegxPattern.matcher(paramString).find()) return ; else next.handle(paramString, paramHttpServletRequest, paramHttpServletResponse, paramArrayOfBoolean); } }