在实现尝试实现的过程中,观察和遇到了这样几个问题。第一个问题是基于jsr-356标准的websocket实现与spring整合的问题,如果不解决这个问题就会导致在websocket的接口里无法自动组装(@Autowired)在spring context里的Bean。第二个问题是当用户离开当前页面时,普通浏览器会立即断开当前页发起的websocket链接,而在微信中的网页不会。在微信中websocket长连接会一直保持链接,直至用户锁屏,或再次发起另一个websocket握手请求时,上一个连接才会断开。这样的现象导致我们不得不在服务端实现心跳检测来侦测用户是否真的离开了目标页面。
OK, SHOW ME THE CODE!
/**
*
* websocket heart break endpoint
* client side viewing-time count
*
* @author zhiheng
*
*/
@ServerEndpoint(value = "/websocket/count")
public class HeartBreakEndpoint {
private Timer timer;
private volatile boolean isPong;
private long startTime;
@OnOpen
public void start(final Session localSession) {
this.startTime = System.currentTimeMillis();
timer.schedule(new TimerTask() {
@Override
public void run() {
try {
if(isPong) {
System.out.println("Timer - Say hi");
localSession.getBasicRemote().sendText("hi");
isPong = false;
} else {
System.out.println("INFO - Check the time.");
try {
localSession.close();
} catch (IOException e) {
e.printStackTrace();
}
this.cancel();
}
} catch (IOException e) {
e.printStackTrace();
this.cancel();
}
}
}, 2000, 2000);
}
@OnClose
public void onClose() {
long currTime = System.currentTimeMillis();
System.out.println("OnClose - view time " + (currTime - this.startTime)/1000 + " sec.");
}
@OnMessage
public void onMessage(String msg) {
System.out.println("OnMessage - " + msg);
if(msg.equals("yes")) {
this.isPong = true;
}
}
@OnError
public void onError(Throwable t) throws Throwable {
}
public HeartBreakEndpoint() {
this.timer = new Timer();
this.isPong = true;
}
}
首先需要明确的一点是,如果在不加其他的配置的前提下,websocket的服务端endpoint默认是多例模式——一个握手连接就会
Hello See heartBreak
客户端的逻辑则比较简单,页面一加载完成就发起websocket握手请求。之后当接到服务端的Ping,就立即返回Pong,如此往
import org.springframework.web.socket.server.endpoint.SpringConfigurator;
@ServerEndpoint(value = "/websocket/count", configurator = SpringConfigurator.class)
在endpoint类上导入这个包,并改一下annotation就可以了。
package com.zhheng.nosencebase.wshandler;
import org.springframework.web.socket.CloseStatus;
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketMessage;
import org.springframework.web.socket.WebSocketSession;
import org.springframework.web.socket.handler.TextWebSocketHandler;
import com.zhheng.nosencebase.dao.CitiesDao;
public class GreetHandler extends TextWebSocketHandler {
private final CitiesDao citiesDao;
@Override
protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {
String msg = citiesDao.selectCitiesList().get(0).getCity();
session.sendMessage(new TextMessage(msg));
}
@Override
public void afterConnectionEstablished(WebSocketSession session) throws Exception {
}
@Override
public void handleMessage(WebSocketSession session, WebSocketMessage> message) throws Exception {
super.handleMessage(session, message);
}
@Override
public void handleTransportError(WebSocketSession session, Throwable exception) throws Exception {
super.handleTransportError(session, exception);
}
@Override
public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {
super.afterConnectionClosed(session, status);
}
public GreetHandler(CitiesDao citiesDao) {
this.citiesDao = citiesDao;
}
}
package com.zhheng.nosencebase;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.WebSocketHandler;
import org.springframework.web.socket.config.annotation.EnableWebSocket;
import org.springframework.web.socket.config.annotation.WebSocketConfigurer;
import org.springframework.web.socket.config.annotation.WebSocketHandlerRegistry;
import com.zhheng.nosencebase.dao.CitiesDao;
import com.zhheng.nosencebase.wshandler.GreetHandler;
@Configuration
@EnableWebSocket
public class WebSocketConfig implements WebSocketConfigurer {
@Autowired
private CitiesDao citiesDao;
public WebSocketConfig() {
}
@Override
public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
registry.addHandler(greetingHandler(), "/wstest");
}
@Bean
public WebSocketHandler greetingHandler() {
return new GreetHandler(citiesDao);
}
}