java 实现websocket的两种方式

简单说明

1.两种方式,一种使用tomcat的websocket实现,一种使用spring的websocket

2.tomcat的方式需要tomcat 7.x,JEE7的支持。

3.spring与websocket整合需要spring 4.x,并且使用了socketjs,对不支持websocket的浏览器可以模拟websocket使用


方式一:tomcat

使用这种方式无需别的任何配置,只需服务端一个处理类,


服务器端代码

[java]
  1. package com.Socket;  
  2.   
  3. import java.io.IOException;  
  4. import java.util.Map;  
  5. import java.util.concurrent.ConcurrentHashMap;  
  6. import javax.websocket.*;  
  7. import javax.websocket.server.PathParam;  
  8. import javax.websocket.server.ServerEndpoint;  
  9. import net.sf.json.JSONObject;  
  10.   
  11. @ServerEndpoint("/websocket/{username}")  
  12. public class WebSocket {  
  13.   
  14.     private static int onlineCount = 0;  
  15.     private static Map<String, WebSocket> clients = new ConcurrentHashMap<String, WebSocket>();  
  16.     private Session session;  
  17.     private String username;  
  18.       
  19.     @OnOpen  
  20.     public void onOpen(@PathParam("username") String username, Session session) throws IOException {  
  21.   
  22.         this.username = username;  
  23.         this.session = session;  
  24.           
  25.         addOnlineCount();  
  26.         clients.put(username, this);  
  27.         System.out.println("已连接");  
  28.     }  
  29.   
  30.     @OnClose  
  31.     public void onClose() throws IOException {  
  32.         clients.remove(username);  
  33.         subOnlineCount();  
  34.     }  
  35.   
  36.     @OnMessage  
  37.     public void onMessage(String message) throws IOException {  
  38.   
  39.         JSONObject jsonTo = JSONObject.fromObject(message);  
  40.           
  41.         if (!jsonTo.get("To").equals("All")){  
  42.             sendMessageTo("给一个人", jsonTo.get("To").toString());  
  43.         }else{  
  44.             sendMessageAll("给所有人");  
  45.         }  
  46.     }  
  47.   
  48.     @OnError  
  49.     public void onError(Session session, Throwable error) {  
  50.         error.printStackTrace();  
  51.     }  
  52.   
  53.     public void sendMessageTo(String message, String To) throws IOException {  
  54.         // session.getBasicRemote().sendText(message);  
  55.         //session.getAsyncRemote().sendText(message);  
  56.         for (WebSocket item : clients.values()) {  
  57.             if (item.username.equals(To) )  
  58.                 item.session.getAsyncRemote().sendText(message);  
  59.         }  
  60.     }  
  61.       
  62.     public void sendMessageAll(String message) throws IOException {  
  63.         for (WebSocket item : clients.values()) {  
  64.             item.session.getAsyncRemote().sendText(message);  
  65.         }  
  66.     }  
  67.       
  68.       
  69.   
  70.     public static synchronized int getOnlineCount() {  
  71.         return onlineCount;  
  72.     }  
  73.   
  74.     public static synchronized void addOnlineCount() {  
  75.         WebSocket.onlineCount++;  
  76.     }  
  77.   
  78.     public static synchronized void subOnlineCount() {  
  79.         WebSocket.onlineCount--;  
  80.     }  
  81.   
  82.     public static synchronized Map<String, WebSocket> getClients() {  
  83.         return clients;  
  84.     }  
  85. }  


客户端js

[javascript]
  1. var websocket = null;  
  2. var username = localStorage.getItem("name");  
  3.   
  4. //判断当前浏览器是否支持WebSocket  
  5. if ('WebSocket' in window) {  
  6.     websocket = new WebSocket("ws://" + document.location.host + "/WebChat/websocket/" + username + "/"+ _img);  
  7. else {  
  8.     alert('当前浏览器 Not support websocket')  
  9. }  
  10.   
  11. //连接发生错误的回调方法  
  12. websocket.onerror = function() {  
  13.     setMessageInnerHTML("WebSocket连接发生错误");  
  14. };  
  15.   
  16. //连接成功建立的回调方法  
  17. websocket.onopen = function() {  
  18.     setMessageInnerHTML("WebSocket连接成功");  
  19. }  
  20.   
  21. //接收到消息的回调方法  
  22. websocket.onmessage = function(event) {  
  23.     setMessageInnerHTML(event.data);  
  24. }  
  25.   
  26. //连接关闭的回调方法  
  27. websocket.onclose = function() {  
  28.     setMessageInnerHTML("WebSocket连接关闭");  
  29. }  
  30.   
  31. //监听窗口关闭事件,当窗口关闭时,主动去关闭websocket连接,防止连接还没断开就关闭窗口,server端会抛异常。  
  32. window.onbeforeunload = function() {  
  33.     closeWebSocket();  
  34. }  
  35.   
  36. //关闭WebSocket连接  
  37. function closeWebSocket() {  
  38.     websocket.close();  
  39. }  


发送消息只需要使用websocket.send("发送消息"),就可以触发服务端的onMessage()方法,当连接时,触发服务器端onOpen()方法,此时也可以调用发送消息的方法去发送消息。关闭websocket时,触发服务器端onclose()方法,此时也可以发送消息,但是不能发送给自己,因为自己的已经关闭了连接,但是可以发送给其他人。


方法二:spring整合

此方式基于spring mvc框架,相关配置可以看我的相关博客文章


WebSocketConfig.java

这个类是配置类,所以需要在spring mvc配置文件中加入对这个类的扫描,第一个addHandler是对正常连接的配置,第二个是如果浏览器不支持websocket,使用socketjs模拟websocket的连接。

[java]
  1. package com.websocket;  
  2.   
  3. import org.springframework.context.annotation.Bean;  
  4. import org.springframework.context.annotation.Configuration;  
  5. import org.springframework.web.socket.config.annotation.EnableWebSocket;  
  6. import org.springframework.web.socket.config.annotation.WebSocketConfigurer;  
  7. import org.springframework.web.socket.config.annotation.WebSocketHandlerRegistry;  
  8. import org.springframework.web.socket.handler.TextWebSocketHandler;  
  9.   
  10. @Configuration  
  11. @EnableWebSocket  
  12. public class WebSocketConfig implements WebSocketConfigurer {  
  13.     @Override  
  14.     public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {  
  15.         registry.addHandler(chatMessageHandler(),"/webSocketServer").addInterceptors(new ChatHandshakeInterceptor());  
  16.         registry.addHandler(chatMessageHandler(), "/sockjs/webSocketServer").addInterceptors(new ChatHandshakeInterceptor()).withSockJS();  
  17.     }  
  18.    
  19.     @Bean  
  20.     public TextWebSocketHandler chatMessageHandler(){  
  21.         return new ChatMessageHandler();  
  22.     }  
  23.   
  24. }  



ChatHandshakeInterceptor.java

这个类的作用就是在连接成功前和成功后增加一些额外的功能,Constants.java类是一个工具类,两个常量。

[java]
  1. package com.websocket;  
  2.   
  3. import java.util.Map;  
  4. import org.apache.shiro.SecurityUtils;  
  5. import org.springframework.http.server.ServerHttpRequest;  
  6. import org.springframework.http.server.ServerHttpResponse;  
  7. import org.springframework.web.socket.WebSocketHandler;  
  8. import org.springframework.web.socket.server.support.HttpSessionHandshakeInterceptor;  
  9.   
  10. public class ChatHandshakeInterceptor extends HttpSessionHandshakeInterceptor {  
  11.   
  12.     @Override  
  13.     public boolean beforeHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler,  
  14.             Map<String, Object> attributes) throws Exception {  
  15.         System.out.println("Before Handshake");  
  16.         /* 
  17.          * if (request instanceof ServletServerHttpRequest) { 
  18.          * ServletServerHttpRequest servletRequest = (ServletServerHttpRequest) 
  19.          * request; HttpSession session = 
  20.          * servletRequest.getServletRequest().getSession(false); if (session != 
  21.          * null) { //使用userName区分WebSocketHandler,以便定向发送消息 String userName = 
  22.          * (String) session.getAttribute(Constants.SESSION_USERNAME); if 
  23.          * (userName==null) { userName="default-system"; } 
  24.          * attributes.put(Constants.WEBSOCKET_USERNAME,userName); 
  25.          *  
  26.          * } } 
  27.          */  
  28.   
  29.         //使用userName区分WebSocketHandler,以便定向发送消息(使用shiro获取session,或是使用上面的方式)  
  30.         String userName = (String) SecurityUtils.getSubject().getSession().getAttribute(Constants.SESSION_USERNAME);  
  31.         if (userName == null) {  
  32.             userName = "default-system";  
  33.         }  
  34.         attributes.put(Constants.WEBSOCKET_USERNAME, userName);  
  35.   
  36.         return super.beforeHandshake(request, response, wsHandler, attributes);  
  37.     }  
  38.   
  39.     @Override  
  40.     public void afterHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler,  
  41.             Exception ex) {  
  42.         System.out.println("After Handshake");  
  43.         super.afterHandshake(request, response, wsHandler, ex);  
  44.     }  
  45.   
  46. }  



ChatMessageHandler.java

这个类是对消息的一些处理,比如是发给一个人,还是发给所有人,并且前端连接时触发的一些动作

[java]
  1. package com.websocket;  
  2.   
  3. import java.io.IOException;  
  4. import java.util.ArrayList;  
  5. import org.apache.log4j.Logger;  
  6. import org.springframework.web.socket.CloseStatus;  
  7. import org.springframework.web.socket.TextMessage;  
  8. import org.springframework.web.socket.WebSocketSession;  
  9. import org.springframework.web.socket.handler.TextWebSocketHandler;  
  10.   
  11. public class ChatMessageHandler extends TextWebSocketHandler {  
  12.   
  13.     private static final ArrayList<WebSocketSession> users;// 这个会出现性能问题,最好用Map来存储,key用userid  
  14.     private static Logger logger = Logger.getLogger(ChatMessageHandler.class);  
  15.   
  16.     static {  
  17.         users = new ArrayList<WebSocketSession>();  
  18.     }  
  19.   
  20.     /** 
  21.      * 连接成功时候,会触发UI上onopen方法 
  22.      */  
  23.     @Override  
  24.     public void afterConnectionEstablished(WebSocketSession session) throws Exception {  
  25.         System.out.println("connect to the websocket success......");  
  26.         users.add(session);  
  27.         // 这块会实现自己业务,比如,当用户登录后,会把离线消息推送给用户  
  28.         // TextMessage returnMessage = new TextMessage("你将收到的离线");  
  29.         // session.sendMessage(returnMessage);  
  30.     }  
  31.   
  32.     /** 
  33.      * 在UI在用js调用websocket.send()时候,会调用该方法 
  34.      */  
  35.     @Override  
  36.     protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {  
  37.         sendMessageToUsers(message);  
  38.         //super.handleTextMessage(session, message);  
  39.     }  
  40.   
  41.     /** 
  42.      * 给某个用户发送消息 
  43.      * 
  44.      * @param userName 
  45.      * @param message 
  46.      */  
  47.     public void sendMessageToUser(String userName, TextMessage message) {  
  48.         for (WebSocketSession user : users) {  
  49.             if (user.getAttributes().get(Constants.WEBSOCKET_USERNAME).equals(userName)) {  
  50.                 try {  
  51.                     if (user.isOpen()) {  
  52.                         user.sendMessage(message);  
  53.                     }  
  54.                 } catch (IOException e) {  
  55.                     e.printStackTrace();  
  56.                 }  
  57.                 break;  
  58.             }  
  59.         }  
  60.     }  
  61.   
  62.     /** 
  63.      * 给所有在线用户发送消息 
  64.      * 
  65.      * @param message 
  66.      */  
  67.     public void sendMessageToUsers(TextMessage message) {  
  68.         for (WebSocketSession user : users) {  
  69.             try {  
  70.                 if (user.isOpen()) {  
  71.                     user.sendMessage(message);  
  72.                 }  
  73.             } catch (IOException e) {  
  74.                 e.printStackTrace();  
  75.             }  
  76.         }  
  77.     }  
  78.   
  79.     @Override  
  80.     public void handleTransportError(WebSocketSession session, Throwable exception) throws Exception {  
  81.         if (session.isOpen()) {  
  82.             session.close();  
  83.         }  
  84.         logger.debug("websocket connection closed......");  
  85.         users.remove(session);  
  86.     }  
  87.   
  88.     @Override  
  89.     public void afterConnectionClosed(WebSocketSession session, CloseStatus closeStatus) throws Exception {  
  90.         logger.debug("websocket connection closed......");  
  91.         users.remove(session);  
  92.     }  
  93.   
  94.     @Override  
  95.     public boolean supportsPartialMessages() {  
  96.         return false;  
  97.     }  
  98.   
  99. }  



spring-mvc.xml

正常的配置文件,同时需要增加对WebSocketConfig.java类的扫描,并且增加

[html]
  1. xmlns:websocket="http://www.springframework.org/schema/websocket"  
  2.               http://www.springframework.org/schema/websocket   
  3.               <target="_blank" href="http://www.springframework.org/schema/websocket/spring-websocket-4.1.xsd">http://www.springframework.org/schema/websocket/spring-websocket-4.1.xsd</a>  



客户端

[html]
  1. <script type="text/javascript"  
  2.         src="http://localhost:8080/Bank/js/sockjs-0.3.min.js"></script>  
  3.     <script>  
  4.         var websocket;  
  5.       
  6.         if ('WebSocket' in window) {  
  7.             websocket = new WebSocket("ws://" + document.location.host + "/Bank/webSocketServer");  
  8.         } else if ('MozWebSocket' in window) {  
  9.             websocket = new MozWebSocket("ws://" + document.location.host + "/Bank/webSocketServer");  
  10.         } else {  
  11.             websocket = new SockJS("http://" + document.location.host + "/Bank/sockjs/webSocketServer");  
  12.         }  
  13.       
  14.         websocket.onopen = function(evnt) {};  
  15.         websocket.onmessage = function(evnt) {  
  16.             $("#test").html("(<font color='red'>" + evnt.data + "</font>)")  
  17.         };  
  18.       
  19.         websocket.onerror = function(evnt) {};  
  20.         websocket.onclose = function(evnt) {}  
  21.       
  22.         $('#btn').on('click', function() {  
  23.             if (websocket.readyState == websocket.OPEN) {  
  24.                 var msg = $('#id').val();  
  25.                 //调用后台handleTextMessage方法  
  26.                 websocket.send(msg);  
  27.             } else {  
  28.                 alert("连接失败!");  
  29.             }  
  30.         });  
  31.     </script>  


注意导入socketjs时要使用地址全称,并且连接使用的是http而不是websocket的ws

posted @ 2017-11-27 13:42  锐洋智能  阅读(95552)  评论(3编辑  收藏  举报