Spring容器整合WebSocket方法一 (spring webSoket API)
案例
Spring+Websocket实现消息的推送
步骤
1、用户登录后建立websocket连接,默认选择websocket连接,如果浏览器不支持,则使用sockjs进行模拟连接
2、建立连接后,服务端返回该用户的未读消息
3、服务端进行相关操作后,推送给某一个用户或者所有用户新消息
相关环境
spring4.0+ , tomcat7+
maven包
<!-- spring websocket库 -->
<dependency> <groupId>org.springframework</groupId> <artifactId>spring-websocket</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-messaging</artifactId> <version>${spring.version}</version> </dependency>
<dependency>
<groupId>javax.websocket</groupId>
<artifactId>javax.websocket-api</artifactId>
<version>1.0</version>
<scope>provided</scope>
</dependency>
Websocet服务端实现
1 @Configuration 2 @EnableWebMvc 3 @EnableWebSocket 4 // 注册websocket server实现类,设置访问websocket,Sockjs的地址 5 public class WebSocketConfig extends WebMvcConfigurerAdapter implements WebSocketConfigurer { 6 @Override 7 public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) { 8 9 //允许连接的域,只能以http或https开头 10 String[] allowsOrigins = {"http://127.0.0.1:8080"}; 11 12 // 支持websocket 的访问链接 13 registry.addHandler(systemWebSocketHandler(), "/echo.send").setAllowedOrigins(allowsOrigins). 14 addInterceptors(handshakeInterceptor()); 15 16 // 不支持websocket的访问链接 17 registry.addHandler(systemWebSocketHandler(), "/sockjs/echo.send").setAllowedOrigins(allowsOrigins). 18 addInterceptors(handshakeInterceptor()).withSockJS(); 19 } 20 21 @Bean 22 public SystemWebSocketHandler systemWebSocketHandler(){ 23 return new SystemWebSocketHandler(); 24 } 25 26 @Bean 27 public HandshakeInterceptor handshakeInterceptor(){ 28 return new HandshakeInterceptor(); 29 } 30 }
SystemWebSocketHandler
1 public class SystemWebSocketHandler extends TextWebSocketHandler{ 2 3 private static final Logger logger = Logger.getLogger(SystemWebSocketHandler.class); 4 5 private static final ArrayList<WebSocketSession> users = new ArrayList<>(); 6 7 @Autowired 8 private WebSocketService webSocketService; 9 10 // 初次链接成功执行 11 @Override 12 public void afterConnectionEstablished(WebSocketSession session) throws Exception { 13 logger.debug("链接成功......"); 14 users.add(session); 15 Integer staffId = (Integer) session.getAttributes().get("staffId"); 16 String exitTime = (String) session.getAttributes().get(staffId+"_exitTime"); 17 logger.debug("staffId:" + staffId); 18 if (staffId != null && staffId != 0) { 19 // 这块会实现自己业务,比如,当用户登录后,会把离线消息推送给用户 20 // 查询未读消息 21 //TextMessage returnMessage = webSocketService.findCheckInfoCount(exitTime); 22 //session.sendMessage(returnMessage); 23 session.sendMessage(new TextMessage("xxxx")); 24 } 25 } 26 27 /** 28 * 接受消息处理消息 js用websocket.send()时候,会调用该方法 29 */ 30 @Override 31 public void handleMessage(WebSocketSession webSocketSession, WebSocketMessage<?> webSocketMessage) throws Exception { 32 33 } 34 35 @Override 36 public void handleTransportError(WebSocketSession session, Throwable throwable) throws Exception { 37 if (session.isOpen()) { 38 session.close(); 39 } 40 logger.info("链接出错,关闭链接......"); 41 users.remove(session); 42 } 43 44 @Override 45 public void afterConnectionClosed(WebSocketSession session, CloseStatus closeStatus) throws Exception { 46 Integer staffId = (Integer) session.getAttributes().get("staffId"); 47 webSocketService.setLogoutTime(staffId); 48 users.remove(session); 49 logger.info("链接关闭......" + closeStatus.toString()+",staffId:"+staffId); 50 } 51 52 @Override 53 public boolean supportsPartialMessages() { 54 return false; 55 } 56 57 58 /** 59 * 给所有在线用户发送消息 60 * 61 * @param message 62 */ 63 public void sendMessageToUsers(TextMessage message) { 64 for (WebSocketSession user : users) { 65 try { 66 if (user.isOpen()) { 67 user.sendMessage(message); 68 } 69 } catch (IOException e) { 70 logger.error(e, e); 71 } 72 } 73 } 74 75 /** 76 * 给某个用户发送消息 77 * 78 * @param userName 79 * @param message 80 */ 81 public void sendMessageToUser(int staffId, TextMessage message) { 82 for (WebSocketSession user : users) { 83 if ((Integer) user.getAttributes().get("staffId") == staffId) { 84 try { 85 if (user.isOpen()) { 86 user.sendMessage(message); 87 } 88 } catch (IOException e) { 89 logger.error(e, e); 90 } 91 break; 92 } 93 } 94 } 95 96 /** 97 * 给某个权限集合的用户发送消息 98 * 99 * @param roleCode 100 * 角色代码 例如:|2| 101 * @param message 102 */ 103 public void sendMessageToRole(String roleCode, TextMessage message) { 104 for (WebSocketSession user : users) { 105 if (((String) user.getAttributes().get("roleCode")).contains(roleCode)) { 106 try { 107 if (user.isOpen()) { 108 user.sendMessage(message); 109 } 110 } catch (IOException e) { 111 logger.error(e, e); 112 } 113 break; 114 } 115 } 116 } 117 }
WebSocketHandshakeInterceptor
// 这个的主要作用是取得当前请求中的用户名,并且保存到当前的WebSocketHandler中,以便确定WebSocketHandler所对应的用户 public class HandshakeInterceptor extends HttpSessionHandshakeInterceptor { private static final Logger logger = Logger.getLogger(HandshakeInterceptor.class); // 进入hander之前的拦截 @Override public boolean beforeHandshake(ServerHttpRequest request, ServerHttpResponse serverHttpResponse, WebSocketHandler webSocketHandler, Map<String, Object> attributes) throws Exception { logger.info("beforeHandshake...start"); if (request instanceof ServletServerHttpRequest) { ServletServerHttpRequest servletRequest = (ServletServerHttpRequest) request; HttpSession session = servletRequest.getServletRequest().getSession(false); //HttpSession session = ((ServletServerHttpRequest) request).getServletRequest().getSession(); if (session != null) { // 使用userName区分WebSocketHandler,以便定向发送消息 如: String userName = (String) session.getAttribute("WEBSOCKET_USERNAME"); //attributes.put("staffId", session.getAttribute("staffId")); //attributes.put("roleCode", session.getAttribute("roleCode")); //attributes.put(session.getAttribute("staffId") + "_exitTime", session.getAttribute(session.getAttribute("staffId") + "_exitTime")); } } logger.info("beforeHandshake...end"); return true; } @Override public void afterHandshake(ServerHttpRequest serverHttpRequest, ServerHttpResponse serverHttpResponse, WebSocketHandler webSocketHandler, Exception e) { logger.debug("afterHandshake...start"); super.afterHandshake(serverHttpRequest, serverHttpResponse, webSocketHandler, e); logger.debug("afterHandshake...end"); } }
socketHelper.js
1 var websocket; 2 $(window).load(function() { 3 socketInit(); 4 }); 5 6 function socketInit(){ 7 if ('WebSocket' in window) { 8 9 websocket = new WebSocket("ws://127.0.0.1:8080/echo.send"); 10 } else if ('MozWebSocket' in window) { 11 12 websocket = new MozWebSocket("ws://127.0.0.1:8080/echo.send"); 13 } else { 14 15 websocket = new SockJS("http://127.0.0.1:8080/echo.send"); 16 } 17 18 websocket.onopen = function(event) { 19 console.log('Info: connection opened.'); 20 }; 21 websocket.onmessage = function(event) { 22 console.log('Received.message: ' + event.data); //处理服务端返回消息 23 }; 24 websocket.onerror = function(event) { 25 alert('Received.error: ' + event.data); 26 }; 27 websocket.onclose = function(event) { 28 console.log('Info: connection closed.'); 29 console.log(event); 30 } 31 }
如果使用注解方式则添加相应注解 也可使用xml配置方式(注解的我没弄好使 错误代码:403 估计也是下面说的访问源问题)
更新applicationContext.xml(默认配置文件,根据自己情况修改即可)的xmlns xmlns:websocket="http://www.springframework.org/schema/websocket" xsi:schemaLocation="http://www.springframework.org/schema/websocket http://www.springframework.org/schema/websocket/spring-websocket.xsd"
配置如下:
<websocket:handlers allowed-origins="*">
<websocket:mapping handler="handler" path="/echo.send"/>
<websocket:handshake-interceptors>
<bean class="com.zhuo.console.core.interceptor.HandshakeInterceptor"/>
</websocket:handshake-interceptors>
</websocket:handlers>
<websocket:handlers allowed-origins="*">
<websocket:mapping handler="handler" path="/echo.send"/>
<websocket:handshake-interceptors>
<bean class="com.zhuo.console.core.interceptor.HandshakeInterceptor"/>
</websocket:handshake-interceptors>
<websocket:sockjs/>
</websocket:handlers>
<bean id="handler" class="com.zhuo.console.websocket.SystemWebSocketHandler" />
注意: allowed-origins="*" spring javadoc的说明是默认情况下,允许所有来源访问,但我跑下来发现不配置allowed-origins的话总是报403错误。 有的说sockjs是不允许有后缀的,否则将无法匹配,这个还没试过等试过后再确认
web.xml中 web-app 的 version 和 xsi:schemaLocation 必须要3.0+
servlet与filter需要加 <async-supported>true</async-supported>
(上面的说必须加我也没试过不加行不行)
配置nginx支持websocket,默认情况下,nginx不支持自动升级至websocket协议,否则js中会出现连接时异常"Error during WebSocket handshake: Unexpected response code: 400",需在恰当的位置加上如下设置: server { listen 8020; location / { proxy_pass http://websocket; proxy_set_header Host $host:8020; #注意, 原host必须配置, 否则传递给后台的值是websocket,端口如果没有输入的话会是80, 这会导致连接失败 proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "Upgrade"; } } upstream websocket { server 192.168.100.10:8081; } 经上述调整后,websocket就可以同时支持通过nginx代理的https协议,结合MQ机制,可以做到B端实时推送、B端/C端实时通信。 nginx的https(自动跳转http->https)+nginx+websocket的完整配置可参考http://www.cnblogs.com/zhjh256/p/6262620.html。
参考链接:https://my.oschina.net/ldl123292/blog/304360
http://www.cnblogs.com/zhjh256/p/6052102.html
浙公网安备 33010602011771号