zoukankan      html  css  js  c++  java
  • java 实现websocket的两种方式实例详解

    目录

     

    一、介绍

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

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

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

     

    二、方式一:tomcat

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

     服务器端代码

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

    客户端js

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

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

     

    三、方法二:spring整合

    WebSocketConfig.java

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

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

    ChatHandshakeInterceptor.java

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

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

    ChatMessageHandler.java

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

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

    spring-mvc.xml

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

    1
    2
    3
        http://www.springframework.org/schema/websocket

    客户端

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

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

  • 相关阅读:
    Message Flood SDUT 1500
    SDUT1482——二元多项式
    SDUT—2057 模拟题
    Eight(bfs+全排列的哈希函数)
    Pick-up sticks(判断两条线段是否相交)
    poj 1265 Area(pick定理)
    poj 2503 Babelfish(字典树哈希)
    poj 3007 Organize Your Train part II(静态字典树哈希)
    Surprising Strings(map类)
    Strange Way to Express Integers (一般模线性方程组)
  • 原文地址:https://www.cnblogs.com/zhuyeshen/p/12201941.html
Copyright © 2011-2022 走看看