首先遇到这个问题有点奇葩,出现在项目上线时的客户现场,头两天一直都无法确定原因,因为它的表现方式很奇怪,基于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