1. 程式人生 > >SSH 項目中 使用websocket 實現網頁聊天功能

SSH 項目中 使用websocket 實現網頁聊天功能

發送 ref asi action 監聽器 remote static scrollto 連接

參考文章 :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         

配置成功後界面如下

技術分享

SSH 項目中 使用websocket 實現網頁聊天功能