首先遇到這個問題有點奇葩,出現在項目上線時的客戶現場,頭兩天一直都無法確定原因,因為它的表現方式很奇怪,基於springboot實現的websocket,同樣的代碼在公司研發環境不會有問題,客戶現場會出現瀏覽器一連接就馬上斷開,沒有使用任何代理服務器,服務器沒有任何異常,就是瀏覽器直接斷開,最后排除現場環境和公司環境差異性,不斷保持兩邊的一直性,最有可能的一項,能想到的人不多了,IP地址不一樣,我一開始是不相信的,IP地址不一樣會導致這種問題?
我注重測試驗證理論,試一把再說,結果就可以了,同樣的服務器在192.168.x.x這種網段下不會有問題,如果是34.69.x.x這種短IP就不行,本人對網絡不是很了解,同樣的服務器和一模一樣的代碼,僅僅一個IP不同就會造成websocket無法使用嗎?我知道的方法和網上的參考資料全部試了一下,無法解決,如果有知道的朋友可以留言。
此路不同就換一條路,我決定放棄springboot的websocket實現,嘗試tomcat服務器的websocket實現,解決了此問題。
pom依賴:
<!-- SpringWebSocket依賴 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
<exclusions>
<exclusion>
<groupId>org.springframework</groupId>
<artifactId>spring-messaging</artifactId>
</exclusion>
</exclusions>
</dependency>
@Configuration public class WebSocketConfig { @Bean public ServerEndpointExporter serverEndpointExporter() { return new ServerEndpointExporter(); } }
java代碼
import com.bootdo.common.utils.JSONUtils; import com.bootdo.system.domain.UserDO; import org.apache.shiro.subject.SimplePrincipalCollection; import org.apache.shiro.subject.support.DefaultSubjectContext; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Component; import javax.servlet.http.HttpSession; import javax.websocket.*; import javax.websocket.server.ServerEndpoint; import java.io.IOException; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; @ServerEndpoint(value = "/endpointChat", configurator=GetHttpSessionConfigurator.class) @Component public class WebSocketSession { private static final Logger logger = LoggerFactory.getLogger(WebSocketSession.class); private static Map<String, WebSocketSession> clients = new ConcurrentHashMap<String, WebSocketSession>(); private Session session; private String userSessionId; private static String mark = "_"; private static String destinationPrefix = "/user"; private List<String> theme = new ArrayList<>(); @OnOpen public void onOpen(Session session,EndpointConfig config) { HttpSession httpSession= (HttpSession) config.getUserProperties().get(HttpSession.class.getName()); SimplePrincipalCollection principalCollection = (SimplePrincipalCollection) httpSession .getAttribute(DefaultSubjectContext.PRINCIPALS_SESSION_KEY); UserDO userDO = (UserDO) principalCollection.getPrimaryPrincipal(); session.setMaxTextMessageBufferSize(1024 * 100 * 5); session.setMaxBinaryMessageBufferSize(1024 * 100 * 5); this.userSessionId = userDO.getUserId().toString()+mark+session.getId(); this.session = session; clients.put(this.userSessionId, this); } @OnClose public void onClose(Session session) { try { clients.remove(userSessionId); }catch (Exception e){ logger.error(e.getMessage(),e); } } @OnError public void onError(Session session, Throwable error) { clients.remove(userSessionId); try { session.close(); } catch (IOException e) { logger.error("close session failed {}", e.getMessage()); } } /** * 收到客戶端消息后調用的方法 * @param message */ @OnMessage public void onMessage(String message, Session session) { SimpleTextMsg msgObj = JSONUtils.jsonToBean(message, SimpleTextMsg.class); if(msgObj.getOption().equals(WebSocketClientOption.REGISTER_THEME)){//client在連接成功之后,注冊監聽主題 theme.add(msgObj.getTheme()); } } public static void convertAndSendToUser(String userId, String theme, String jsonData) { synchronized (userId.intern()){ try{ for (WebSocketSession item : clients.values()) { String idKey = userId+mark+item.getUserSessionId().split(mark)[1]; if (item.getUserSessionId().equals(idKey)){ if(item.getTheme().contains(destinationPrefix+theme)){//判斷該session的用戶是否訂閱主題 SimpleTextMsg textMsg = new SimpleTextMsg(); textMsg.setBody(jsonData); textMsg.setTheme("/user"+theme); item.session.getBasicRemote().sendText(JSONUtils.beanToJson(textMsg)); } } } }catch (Exception e){ logger.error(e.getMessage(),e); } } } public String getUserSessionId() { return userSessionId; } public void setUserSessionId(String userSessionId) { this.userSessionId = userSessionId; } public List<String> getTheme() { return theme; } public void setTheme(List<String> theme) { this.theme = theme; } public static Map<String, WebSocketSession> getClients() { return clients; } }
html5_websocket.js 核心方法
//開始之前請引入reconnecting-websocket.js var ipRe=/^(\d+)\.(\d+)\.(\d+)\.(\d+).*$/;//正則表達式 function SxpSockJS(url){ this.host = window.location.host; if(ipRe.test(url)){ this.url = url; }else if(url.indexOf("http") == 0 || url.indexOf("https") == 0){ this.url = url.splice("//")[1]; }else{ this.url = this.host + url; } console.log("connection url:"+this.url); this.websocket = null; this.subscribe = function(theme, callBack){//theme訂閱主題,callBack回調函數 this.websocket.themeMap[theme] = callBack; this.websocket.send("{theme:'"+theme+"',option:'registerTheme'}"); }; this.connect = function(onOpen,onError){ this.websocket = new ReconnectingWebSocket("ws://"+this.url,null,{reconnectInterval: 3000}); this.websocket.themeMap = {}; this.websocket.timeoutInterval = 5400; this.websocket.onopen = function(event){ console.log("漂亮!與服務器websocket連接成功!!!"); if(onOpen && typeof onOpen === "function")onOpen(); }; this.websocket.onclose = function(event){ console.log("悲劇!與服務器websocket連接斷開!!!"); if(onError && typeof onOpen === "function")onError(); }; this.websocket.onmessage = function (data) { console.log(data.timeStamp+"<<< CONNECTED"); var obj = eval('(' + data.data + ')'); var callBack = this.themeMap[obj.theme]; if(callBack){ console.log("theme:"+obj.theme); console.log("jsonData:"+obj.body); callBack(obj); } console.log(data.timeStamp+"<<< END"); console.log(" "); } } }
js調用代碼
function init() { var sock = new SxpSockJS(ctx+"endpointChat"); sock.connect(function() {//異步 sock.subscribe("/user/queue/prisoncellOnLine", handleNotification); sock.subscribe("/user/queue/prisoncellAreaWarn", personAreaWarn); sock.subscribe("/user/queue/prisoncellOffLine", personOffLine); sock.subscribe("/user/queue/personSignEnd", personSignEnd); sock.subscribe("/user/queue/personSignStart", personSignStart); sock.subscribe("/user/queue/prisoncellFlush",prisoncellFlush); },function(){ //異常處理邏輯 }); }
這里用到了一個websocket重連的函數,需要下載一個js,reconnecting-websocket.min.js