首先遇到這個問題有點奇葩,出現在項目上線時的客戶現場,頭兩天一直都無法確定原因,因為它的表現方式很奇怪,基於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
