zoukankan      html  css  js  c++  java
  • SSH 项目中 使用websocket 实现网页聊天功能

    参考文章  :java使用websocket,并且获取HttpSession,源码分析    http://www.cnblogs.com/zhuxiaojie/p/6238826.html

    1.在项目中引入依赖

    websocket遵循了javaee规范,所以需要引入javaee的包

     1      <dependency>
     2         <groupId>javax.websocket</groupId>
     3         <artifactId>javax.websocket-api</artifactId>
     4         <version>1.1</version>
     5         <scope>provided</scope>
     6     </dependency>
     7     <dependency>
     8         <groupId>javax</groupId>
     9         <artifactId>javaee-api</artifactId>
    10         <version>7.0</version>
    11     </dependency>

    2.编写一个处理websocket请求的类

      1 import java.io.IOException;
      2 import java.util.Set;
      3 import java.util.concurrent.CopyOnWriteArraySet;
      4 import java.util.concurrent.atomic.AtomicInteger;
      5 
      6 import javax.servlet.http.HttpSession;
      7 import javax.websocket.EndpointConfig;
      8 import javax.websocket.OnClose;
      9 import javax.websocket.OnError;
     10 import javax.websocket.OnMessage;
     11 import javax.websocket.OnOpen;
     12 import javax.websocket.Session;
     13 import javax.websocket.server.ServerEndpoint;
     14 
     15 import org.apache.commons.logging.Log;
     16 import org.apache.commons.logging.LogFactory;
     17 
     18 import com.itheima.bos.domain.User;
     19 import com.itheima.bos.utils.HTMLFilter;  
     20 
     21 
     22 // 进行配置  websocket 通过下面的地址链接服务器
     23 @ServerEndpoint(value = "/ws/chat" ,configurator = HttpSessionConfigurator.class)  
     24 public class ChatAnnotation {  
     25 
     26   private static final Log log = LogFactory.getLog(ChatAnnotation.class);  
     27 
     28   private static final String GUEST_PREFIX = "用户";  
     29   private static final AtomicInteger connectionIds = new AtomicInteger(0);    //统计及在线人数
     30   private static final Set<ChatAnnotation> connections =   new CopyOnWriteArraySet<ChatAnnotation>();  
     31 
     32   private final String nickname;  
     33   private Session session;  
     34   private HttpSession httpSession;//httpsession  手动添加进来的值
     35   private User user = null;
     36   
     37 
     38   public Session getSession() {
     39     return session;
     40 }
     41 
     42 public void setSession(Session session) {
     43     this.session = session;
     44 }
     45 
     46 public HttpSession getHttpSession() {
     47     return httpSession;
     48 }
     49 
     50 public void setHttpSession(HttpSession httpSession) {
     51     this.httpSession = httpSession;
     52 }
     53 
     54 public User getUser() {
     55     return user;
     56 }
     57 
     58 public void setUser(User user) {
     59     this.user = user;
     60 }
     61 
     62 public ChatAnnotation() {  
     63       nickname = GUEST_PREFIX + connectionIds.getAndIncrement();  
     64   }  
     65 
     66   /*当websocket的客户端连接到服务器时候,这个方法就会执行,并且传递一个session会话对象来 
     67    我们拿到这话session,就是可以给客户端发送消息*/  
     68   @OnOpen  
     69   public void start(Session session,EndpointConfig config) {  
     70        HttpSession httpSession= (HttpSession) config.getUserProperties().get(HttpSession.class.getName());
     71       user = (User)httpSession.getAttribute("user");   //如果已经登录,在别的action中已经将一个user对象存入session中,此处直接取出
     72        if(user != null){
     73            //TODO  判断登录的用户是否已经存在于连接中
     74            this.session = session;
     75            this.httpSession = httpSession;
     76            connections.add(this);  
     77            String message = String.format(" 用户  %s  %s , 当前在线人数 %s", user.getUsername(), "加入聊天.",connectionIds);  
     78            broadcast(message);  
     79            
     80        }else{
     81            //用户未登陆
     82            try {
     83                session.close();
     84            } catch (IOException e) {
     85                e.printStackTrace();
     86            }
     87        }
     88   }  
     89 
     90     
     91   public static Set<ChatAnnotation> getConnections() {
     92     return connections;
     93 }
     94 
     95   /*客户端被关闭时候,就会自动会调用这个方法*/  
     96   @OnClose  
     97   public void end() {  
     98       connections.remove(this);  
     99       String message = String.format("- %s %s %s", user.getUsername(), "已经下线,当前用户数量是 ",connectionIds.decrementAndGet());  
    100       broadcast(message);  
    101   }  
    102 
    103   /*客户端给服务器发送消息,这个方法会自动调用,并且可以拿到发送过来的数据*/  
    104   @OnMessage  
    105   public void incoming(String message) {  
    106       // Never trust the client  
    107       String filteredMessage = String.format("%s: %s",  
    108               user.getUsername(), HTMLFilter.filter(message.toString()));  
    109       broadcast(filteredMessage);  
    110   }  
    111 
    112   /*发生了异常自动执行*/  
    113   @OnError  
    114   public void onError(Throwable t) throws Throwable {  
    115       log.error("Chat Error: " + t.toString(), t);  
    116   }  
    117 
    118   /*广播:遍历客户端集,发送消息,注意发送要用的session,用session.getBasicRemote().sendText(msg)发送消息*/  
    119   private static void broadcast(String msg) {  
    120       for (ChatAnnotation client : connections) {//遍历所有  
    121           try {//如果这个client已经在线  
    122               synchronized (client) {  
    123                   client.session.getBasicRemote().sendText(msg);//发送消息  
    124               }  
    125           } catch (IOException e) {//如果这个client不在线  
    126               log.debug("Chat Error: 向用户"+client.getUser().getUsername()+"发送消息失败", e);  
    127               connections.remove(client);  
    128               try {  
    129                   client.session.close();  
    130               } catch (IOException e1) {  
    131                   // Ignore  
    132               }  
    133               String message = String.format("-- %s %s", client.user.getUsername(), "已经下线.");  
    134               broadcast(message);  
    135           }  
    136       }  
    137   }
    138 
    139     @Override
    140     public int hashCode() {
    141         final int prime = 31;
    142         int result = 1;
    143         result = prime * result + ((user == null) ? 0 : user.hashCode());
    144         return result;
    145     }
    146     
    147     @Override
    148     public boolean equals(Object obj) {
    149         if (this == obj)
    150             return true;
    151         if (obj == null)
    152             return false;
    153         if (getClass() != obj.getClass())
    154             return false;
    155         ChatAnnotation other = (ChatAnnotation) obj;
    156         if (user == null) {
    157             if (other.user != null)
    158                 return false;
    159         } else if (!user.equals(other.user))
    160             return false;
    161         return true;
    162     }  
    163       
    164 }  

    3.编写获取httpsesion的类,和配置这些类

    由于HTTP协议与websocket协议的不同,导致没法直接从websocket中获取协议,我们必须手动添加,具体细节可参考   获取httpsession 源码分析  

     1 import javax.servlet.http.HttpSession;
     2 import javax.websocket.HandshakeResponse;
     3 import javax.websocket.server.HandshakeRequest;
     4 import javax.websocket.server.ServerEndpointConfig;
     5 import javax.websocket.server.ServerEndpointConfig.Configurator;
     6 
     7 /**
     8  * 从websocket中获取用户session
     9  * 由于HTTP协议与websocket协议的不同,导致没法直接从websocket中获取协议,
    10  * 下面的类中写了获取HttpSession的代码,但是如果真的放出去执行,那么会报空指值异常,因为这个HttpSession并没有设置进去。
    11     需要我们自己来来设置HttpSession。这时候我们需要写一个监听器  下面有监听器的代码。
    12  *
    13  */
    14 public class HttpSessionConfigurator extends Configurator {
    15 
    16     @Override
    17     public void modifyHandshake(ServerEndpointConfig sec, HandshakeRequest request, HandshakeResponse response) {
    18 
    19           HttpSession httpSession = (HttpSession) request.getHttpSession();
    20           // ServerEndpointConfig 继承->EndpointConfig  中一个方法  
    21           //  Map<String,Object> getUserProperties();  对这个map进行赋值
    22           sec.getUserProperties().put(HttpSession.class.getName(), httpSession);
    23     }
    24 }

    监听器

     1 import javax.servlet.ServletRequestEvent;
     2 import javax.servlet.ServletRequestListener;
     3 import javax.servlet.http.HttpServletRequest;
     4 
     5 public class RequestListener implements ServletRequestListener {
     6 
     7     public void requestInitialized(ServletRequestEvent sre)  {
     8         //将所有request请求都携带上httpSession
     9         ((HttpServletRequest) sre.getServletRequest()).getSession();
    10     }
    11     public RequestListener() {
    12     }
    13 
    14     public void requestDestroyed(ServletRequestEvent arg0)  {
    15     }
    16 }

    有了监听器我们需要在web.xml中配置它

    1 <listener>
    2       <listener-class>com.xwer.bos.web.websocket.RequestListener</listener-class>
    3   </listener>

    4. 前台页面

      1 <!DOCTYPE html>
      2 <html>
      3 <head>
      4 <meta charset="utf-8">
      5     <title>即时通讯</title>
      6     <style type="text/css">
      7         input#chat {
      8             width: 410px
      9         }
     10 
     11         #console-container {
     12             width: 400px;
     13         }
     14 
     15         #console {
     16             border: 1px solid #CCCCCC;
     17             border-right-color: #999999;
     18             border-bottom-color: #999999;
     19             height: 170px;
     20             overflow-y: scroll;
     21             padding: 5px;
     22             width: 100%;
     23         }
     24 
     25         #console p {
     26             padding: 0;
     27             margin: 0;
     28         }
     29     </style>
     30     <script type="text/javascript">
     31         var Chat = {};
     32 
     33         Chat.socket = null;
     34 
     35         Chat.connect = (function(host) {
     36             if ('WebSocket' in window) {
     37                 Chat.socket = new WebSocket(host);
     38             } else if ('MozWebSocket' in window) {
     39                 Chat.socket = new MozWebSocket(host);
     40             } else {
     41                 Console.log('Error: WebSocket is not supported by this browser.');
     42                 return;
     43             }
     44 
     45             Chat.socket.onopen = function () {
     46                 Console.log('Info: 登录成功');
     47                 document.getElementById('chat').onkeydown = function(event) {
     48                     if (event.keyCode == 13) {
     49                         Chat.sendMessage();
     50                     }
     51                 };
     52             };
     53 
     54             Chat.socket.onclose = function () {
     55                 document.getElementById('chat').onkeydown = null;
     56                 Console.log('Info: 已经下线.');
     57             };
     58 
     59             Chat.socket.onmessage = function (message) {
     60                 Console.log(message.data);
     61             };
     62         });
     63 
     64         Chat.initialize = function() {
     65             var urls=window.location.href;
     66             url = urls.substring(0,urls.lastIndexOf("/")).replace("http:","");
     67             
     68             
     69             if (window.location.protocol == 'http:') {
     70                 Chat.connect('ws://' + url + '/ws/chat');
     71             } else {
     72                 Chat.connect('wss://' + url + '/ws/chat');
     73             }
     74         };
     75 
     76         Chat.sendMessage = (function() {
     77             var message = document.getElementById('chat').value;
     78             if (message != '') {
     79                 Chat.socket.send(message);
     80                 document.getElementById('chat').value = '';
     81             }
     82         });
     83 
     84         var Console = {};
     85 
     86         Console.log = (function(message) {
     87             var console = document.getElementById('console');
     88             var p = document.createElement('p');
     89             p.style.wordWrap = 'break-word';
     90             p.innerHTML = message;
     91             console.appendChild(p);
     92             while (console.childNodes.length > 25) {
     93                 console.removeChild(console.firstChild);
     94             }
     95             console.scrollTop = console.scrollHeight;
     96         });
     97         
     98         //关闭WebSocket连接
     99          function closeWebSocket() {
    100              Chat.socket.close();
    101          }
    102         
    103         //发送消息
    104         function sendMessage(){
    105             Chat.sendMessage();
    106         }
    107         Chat.initialize();
    108 
    109     </script>
    110 </head>
    111 <body>
    112 <noscript><h2 style="color: #ff0000">浏览器不支持websocket</h2></noscript>
    113 <div>
    114     <p>
    115         <input type="text" placeholder="按下 enter键开始聊天 " id="chat">
    116         <button onclick="sendMessage()">发送消息</button>
    117     </p>
    118     <div id="console-container">
    119         <div id="console"></div>
    120     </div>
    121     
    122        <hr/>
    123     <button onclick="closeWebSocket()">关闭连接</button>
    124   <hr/>
    125 </div>
    126 </body>
    127 </html>

    5. 由于使用了Struts框架,在Struts配置文件中配置,不过滤WS请求

    1     <!--不拦截ws请求 -->
    2     <constant name="struts.action.excludePattern" value="/ws/chat"></constant>
    3         

     配置成功后界面如下

     

  • 相关阅读:
    Android 官网提供的Custom-view 编译出错--error: No resource identifier found for attribute
    杀死进程 kill -9
    自定义控件优化
    linux 定时执行任务 crontab
    Android 悬浮窗 WindowManager WindowManager.LayoutParamas
    IntellijIDEA 使用技巧
    [转]Linux 分区 swap
    网络请求 图片请求
    Android app version code and name
    Android发送请求到不同的Servlet,但都是一个Servlet处理
  • 原文地址:https://www.cnblogs.com/xwer/p/7376593.html
Copyright © 2011-2022 走看看