SSH 项目中 使用websocket 实现网页聊天功能
Posted xwer
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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 = (tomcat websocket 实现网页在线即时聊天