背景:当用户登录的时候,建立websocket连接,默认使用websocket连接,如果浏览器不支持(安卓需要4.4以上)则使用sockjs模拟连接;服务器返回对应的用户没有读取的消息;服务端不定时的向某个用户或者某些用户或者全部用户推送消息
分析:需要三个类,第一个实现配置类:继承WebMvcConfigureAdapter实现类WebSocketConfigure用于注册访问的地址,(应该)属于第一次握手;第二个连接和消息处理类:实现WebSocketHandler类,对socket的连接、建立、关闭、消息处理等方法重写;第三个拦截器类:实现HandslerShakerInterceptor类,重写两个方法,拦截和重定向;一个配置文件;
解决方案:
1、首先实现WebSocketHandler类
public class SystemWebSocketHandler implements WebSocketHandler{
//几个方法
private static final Logger logger = Logger.getLogger(WebSocketHander.class);
private static final ArrayListusers = new ArrayList();
//初次链接成功执行
@Override public void afterConnectionEstablished(WebSocketSession session) throws Exception { users.add(session);
}
//接受消息、处理消息
@Override public void handleMessage(WebSocketSession webSocketSession, WebSocketMessage >webSocketMessage) throws Exception {
String userName = (String) webSocketSession.getAttributes().get("WEBSOCKET_USERNAME");
String to_user = (String) webSocketSession.getAttributes().get("WEBSOCKET_TOUSER");
if(to_user.equals("all")){
sendMessageToUsers(new TextMessage(webSocketMessage.getPayload() + ""));
}else if(!to_user.equals("")){
sendMessageToUser(to_user,new TextMessage(webSocketMessage.getPayload() + ""));
}
}
//如果连接抛出异常
@Override public void handleTransportError(WebSocketSession webSocketSession, Throwable throwable) throws Exception {
if(webSocketSession.isOpen()){
webSocketSession.close();
}
logger.debug("链接出错,关闭链接......");
users.remove(webSocketSession);
}
//连接关闭后
@Override public void afterConnectionClosed(WebSocketSession webSocketSession, CloseStatus closeStatus) throws Exception {
logger.debug("链接关闭......" + closeStatus.toString());
users.remove(webSocketSession);
}
@Override public boolean supportsPartialMessages() {
return false;
}
/*******************************自定义方法*********************************************/
/** * 给所有在线用户发送消息 * * @param message */
public void sendMessageToUsers(TextMessage message) {
//遍历消息
for (WebSocketSession user : users) {
try {
if (user.isOpen()) {
user.sendMessage(message);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
/** * 给某个用户发送消息 * * @param userName * @param message */
public void sendMessageToUser(String userName, TextMessage message) {
for (WebSocketSession user : users) {
if (user.getAttributes().get("WEBSOCKET_USERNAME").equals(userName)) {
try {
if (user.isOpen()) {
user.sendMessage(message);
}
} catch (IOException e) {
e.printStackTrace();
}
break;
}
}
}
}
2、实现WebSocketConfigure类
//springmvc的注解,三个
@Configuration
@EnableWebMvc
@EnableWebSocket//开启websocket
public class WebSocketConfig extends WebMvcConfigurerAdapter implements WebSocketConfigurer {
public WebSocketConfig() {
}
@Override
public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
//WebSocketHandler 和 HandshakeInterceptor都是自定义的类
registry.addHandler( systemWebSocketHandler(),"/echo").addInterceptors(new HandshakeInterceptor()).setAllowedOrigins("*"); //支持websocket 的访问链接
registry.addHandler( systemWebSocketHandler(),"/sockjs/echo").addInterceptors(new HandshakeInterceptor()).withSockJS(); //不支持websocket的访问链接
}
public WebSocketHandler systemWebSocketHandler() {
return new WebSocketHandler();
}
}
3、实现HandlerShakeInterceptor类
public class HandshakeInterceptor implements org.springframework.web.socket.server.HandshakeInterceptor {
//进入hander之前的拦截
@Override public boolean beforeHandshake(ServerHttpRequest request, ServerHttpResponse serverHttpResponse, WebSocketHandler webSocketHandler, Mapmap) throws Exception {
if (request instanceof ServletServerHttpRequest) {
// ServletServerHttpRequest servletRequest = (ServletServerHttpRequest) request;
// request.getURI();
String parameter=request.getURI().getQuery();
String openid="";
String to_user="";
if(request.getHeaders().containsKey("Sec-WebSocket-Extensions")) {
request.getHeaders().set("Sec-WebSocket-Extensions", "permessage-deflate");
}
if(parameter!=null){
if(parameter.indexOf("&")>0){ // 发送信息链接
String part[]=parameter.split("&");
String user[]=part[0].split("=");
openid=user[1];
String touser[];
if(part.length==2){
touser=part[1].split("=");
to_user=touser[1];
}
}else{ //会员登录链接
String user[]=parameter.split("=");
if (user.length>1){
openid=user[1];
}else{
openid="guest";
}
}
//使用userName区分WebSocketHandler,以便定向发送消息
map.put("WEBSOCKET_USERNAME",openid);
map.put("WEBSOCKET_TOUSER", to_user);
}
}
return true;
}
@Override
public void afterHandshake(ServerHttpRequest serverHttpRequest, ServerHttpResponse serverHttpResponse, WebSocketHandler webSocketHandler, Exception e) {
}
}
4、配置文件
web-app version="3.0"//此处,之前version=“1.0”,最近后台报错,参考其他作者的资料意思要大于3.0才行
5.再客户端引入sockjs.js
6.在需要推送消息的页面添加代码
var socket_url='ws://*****************/echo?openid='+openid;
$(function(){
var websocket;
websocket= new WebSocket(socket_url);
websocket.onopen = function (evnt) {
console.log("链接服务器成功");
};
//接收消息
websocket.onmessage = function (evnt) {
//
***************************
};
websocket.onerror = function (evnt) {
};
websocket.onclose = function (evnt) {
// alert("与服务器断开了链接!");
websocket= new WebSocket(socket_url);
}
function send(){
if (websocket != null) {
websocket.send(message);
} else {
// alert('未与服务器链接.');
}
}
});
参考文档https://my.oschina.net/ldl123292/blog/304360?p=2&temp=1484634049679#blog-comments-list
http://blog.csdn.net/hllinc8/article/details/49097971