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         

 

 配置成功后界面如下

 

 

 

 

 

 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM