websocket通信主要来自两个类以及一个测试的html页面。
MyHandler 和 WebSocketH5Config,下面全部代码
MyHandler类全部代码:
package com.union.common.config; import java.io.IOException; import java.util.HashMap; import java.util.Map; import java.util.Set; import com.alibaba.fastjson.JSONObject; import org.springframework.stereotype.Service; import org.springframework.web.socket.CloseStatus; import org.springframework.web.socket.TextMessage; import org.springframework.web.socket.WebSocketHandler; import org.springframework.web.socket.WebSocketMessage; import org.springframework.web.socket.WebSocketSession; @Service public class MyHandler implements WebSocketHandler { //在线用户列表 private static final Map<String, WebSocketSession> webSocketUsers; static { webSocketUsers = new HashMap<>(); } //新增socket @Override public void afterConnectionEstablished(WebSocketSession session) throws Exception { String ID = session.getUri().toString().split("uid=")[1]; if (ID != null) { webSocketUsers.put(ID, session); session.sendMessage(new TextMessage("成功建立socket连接")); } } //接收socket信息 @Override public void handleMessage(WebSocketSession webSocketSession, WebSocketMessage<?> webSocketMessage) throws Exception { try{ JSONObject jsonobject = JSONObject.parseObject((String) webSocketMessage.getPayload()); String message = (String) jsonobject.get("message"); if (message == null) { message = "服务器收到了,hello!"; } sendMessageToUser(jsonobject.get("id")+"",message); }catch(Exception e){ e.printStackTrace(); } } /** * 发送信息给指定用户 * @param clientId * @param message * @return */ public boolean sendMessageToUser(String clientId, String message) { if(webSocketUsers.get(clientId) == null){ return false; } WebSocketSession session = webSocketUsers.get(clientId); if(!session.isOpen()) { return false; } try { session.sendMessage(new TextMessage(message)); } catch (IOException e) { e.printStackTrace(); return false; } return true; } /** * 广播信息 * @param message * @return */ public boolean sendMessageToAllUsers(String message) { boolean allSendSuccess = true; Set<String> clientIds = webSocketUsers.keySet(); WebSocketSession session = null; for (String clientId : clientIds) { try { session = webSocketUsers.get(clientId); if (session.isOpen()) { session.sendMessage(new TextMessage(message)); } } catch (IOException e) { e.printStackTrace(); allSendSuccess = false; } } return allSendSuccess; } /** * 关闭连接 * @param clientId * @return */ public void afterConnectionClosed(String clientId) { try { if(webSocketUsers.get(clientId) != null){ WebSocketSession session = webSocketUsers.get(clientId); afterConnectionClosed(session,CloseStatus.NORMAL); } webSocketUsers.remove(clientId); }catch (Exception e){ e.printStackTrace(); } } @Override public void handleTransportError(WebSocketSession session, Throwable exception) throws Exception { String uid = session.getUri().toString().split("uid=")[1]; webSocketUsers.remove(uid); if (session.isOpen()) { session.close(); } } @Override public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception { String uid = session.getUri().toString().split("uid=")[1]; webSocketUsers.remove(uid); session.close(status); } @Override public boolean supportsPartialMessages() { return false; } }
WebSocketH5Config 类全部代码:
package com.union.common.config; import org.springframework.context.annotation.Configuration; import org.springframework.web.socket.config.annotation.EnableWebSocket; import org.springframework.web.socket.config.annotation.WebSocketConfigurer; import org.springframework.web.socket.config.annotation.WebSocketHandlerRegistry; //实现接口来配置Websocket请求的路径和拦截器。 @Configuration @EnableWebSocket public class WebSocketH5Config implements WebSocketConfigurer { @Override public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) { //handler是webSocket的核心,配置入口 registry.addHandler(new MyHandler(), "/myHandler/{uid}").setAllowedOrigins("*"); } }
测试用的html页面:
<!DOCTYPE html> <html> <head> <title>socket.html</title> <meta name="content-type" content="text/html" charset="UTF-8"> </head> <body> Welcome<br/> 登录的uid:<input id="uid" type="text"/><br/> <button onclick="connectWebSocket()">connectWebSocket</button><br/> 发送出去的信息:<input id="text" type="text"/><br/> 发送送给谁的uid:<input id="sendToUid" type="text"/><br/> <button onclick="send()">Send</button><br/> 收到的信息:<input id="getMessage" type="text"/><br/> <button onclick="closeWebSocket()">Close</button> <div id="message"> </div> </body> <script type="text/javascript"> var websocket = null; //强制关闭浏览器 调用websocket.close(),进行正常关闭 window.onunload = function () { //关闭连接 closeWebSocket(); } //建立WebSocket连接 function connectWebSocket() { var userID = document.getElementById("uid"); console.log(userID); console.log("开始..."); //建立webSocket连接 websocket = new WebSocket("ws://localhost:8080/myHandler/uid=" + userID.value); //打开webSokcet连接时,回调该函数 websocket.onopen = function () { console.log("onpen"); } //关闭webSocket连接时,回调该函数 websocket.onclose = function () { //关闭连接 console.log("onclose"); } //接收信息 websocket.onmessage = function (msg) { var getMessage = document.getElementById("getMessage"); getMessage.value = msg.data; console.log(msg.data); } } //发送消息 function send() { var sendToUid = document.getElementById("sendToUid"); var messageText = document.getElementById("text"); var postValue = {}; postValue.id = sendToUid.value; postValue.message = messageText.value; websocket.send(JSON.stringify(postValue)); } //关闭连接 function closeWebSocket() { if (websocket != null) { websocket.close(); } } </script> </html>
因为websocket使用的是ws协议。所以我在nginx上做了一个请求转发,这样服务器就可以使用:
在nginx.conf文件里面的http部分加入如下代码。
map $http_upgrade $connection_upgrade { default upgrade; '' close; }
在server{}部分加了如下代码。放在最前面,就可以在ws部分使用域名访问了:
ws和wss就像http和https一样的
wss://api.****.com/union-front/myHandler/uid=
location ~* /union-front/myHandler { proxy_pass http://127.0.0.1:8080; proxy_redirect off; proxy_set_header X-Real-IP $remote_addr; proxy_set_header Host $host; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection $connection_upgrade; proxy_set_header Origin ""; }
当使用websocket的时候,再使用定时任务,则会报错,在Application启动类上加上如下代码即可解决这个问题
@Bean public TaskScheduler taskScheduler() { //使用 websockt注解的时候,使用@EnableScheduling注解启动的时候一直报错,增加这个bean 则报错解决。 ThreadPoolTaskScheduler taskScheduler = new ThreadPoolTaskScheduler(); taskScheduler.setPoolSize(10); taskScheduler.initialize(); return taskScheduler; }