1. 程式人生 > >springmvc整合websocket

springmvc整合websocket

    最近專案中需要完成資訊的實時顯示功能,因此考慮使用websocket來實現,廢話不多說,直接上配置流程及程式碼。

1.  首先引入需要的jar包,spring和springmvc的自然不多說,主要是引入spring整合的websocket包

               <dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-websocket</artifactId>

<version>${spring.version}</version>

</dependency>


2. 編寫websocket配置類,當然也可以在xml配置(這裡就不多說了)

    編寫自己的WebSocketConfig  extends WebMvcConfigurerAdapter implements WebSocketConfigurer

   編寫這個類主要是為了實現WebSocketConfigurer.registerWebSocketHandlers方法給websocket註冊訊息處理器WebSocketHandler。

具體實現如下:

@Configuration
@EnableWebMvc
@EnableWebSocket
public class WebSocketConfig extends WebMvcConfigurerAdapter implements
WebSocketConfigurer {

@Override
public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
// 前臺 可以使用websocket環境
registry.addHandler(webSocketHandler(), "/websocket").addInterceptors(new HandshakeInterceptor());

// 前臺 不可以使用websocket環境,則使用sockjs進行模擬連線

registry.addHandler(webSocketHandler(),"/sockjs/websocket").addInterceptors(

                        new  HandshakeInterceptor()).withSockJS();

}

// websocket 處理類
@Bean
public WebSocketHandler webSocketHandler() {
return new MySocketHandler();
}

}


3.  編寫websocket訊息處理器MySocketHandler implements WebSocketHandler

這個類是為了處理前端傳送的訊息或推送訊息給前端,也對所有的連線進行管理

public class MyWebSocketHandler implements WebSocketHandler {
private static final Logger log = Logger.getLogger(ICWantWebSocketHandler.class);
@Autowired
RedisUtils redisUtils;
// 儲存所有的使用者session
private static final Map<String, WebSocketSession> users = new HashMap<>();

// 連線 就緒時
@Override
public void afterConnectionEstablished(WebSocketSession session)
throws Exception {
log.info("connect websocket success.......");
        //這塊會實現自己業務,比如,當用戶登入後,會把離線訊息推送給使用者
        //TextMessage returnMessage = new TextMessage("你將收到的離線");
        //session.sendMessage(returnMessage);
String username= (String) session.getAttributes().get("WEBSOCKET_USERNAME");
System.out.println(username);
System.out.println(session.getId());

//判斷是否有同名連線,若存在則關閉
WebSocketSession s = users.get(username);
if(null != s && s.isOpen())
s.close();
users.put(username, session);
System.out.println("connect to the websocket success......當前數量:"+users.size());
}

// 處理前端傳送的訊息資訊
@Override
public void handleMessage(WebSocketSession session,
WebSocketMessage<?> message) throws Exception {
//獲取訊息體
String msg = message.getPayload().toString();
if(msg.startsWith("java.nio.HeapByteBuffer")){
System.out.println("瀏覽器傳送的心跳包");
return;
}

}

// 處理傳輸時異常
@Override
public void handleTransportError(WebSocketSession session,
Throwable exception) throws Exception {

//log.info("資料傳輸異常:  " + exception.getMessage());
}

// 關閉 連線時
@Override
public void afterConnectionClosed(WebSocketSession session,
CloseStatus closeStatus) throws Exception {

log.info("connect websocket closed.......");
String username= (String) session.getAttributes().get("WEBSOCKET_USERNAME");
        System.out.println("使用者"+username+"已退出!");
        users.remove(username);
        System.out.println("剩餘線上使用者" + users.size());
}

@Override
public boolean supportsPartialMessages() {
return false;
}

// 給所有使用者傳送 資訊
public void sendMsgToAllUsers(SocketMessageVO message) {
for (WebSocketSession user : users.values()) {
String msg = GsonUtil.object2JsonStr(message);
TextMessage textMsg = new TextMessage(msg);
try {
user.sendMessage(textMsg);
} catch (IOException e) {
log.error("XLTX-ERROR: websocket傳送訊息出錯", e);
}
}
}

/**
* 傳送訊息給指定的使用者
* @param SocketMessageVO  from_user 或 to_user為null時直接返回
*/
public void sendMsgToUser(SocketMessageVO message) {
if(message == null || message.getFrom_user() == null || message.getTo_user() == null)
return;
String toUser = message.getTo_user();

WebSocketSession session = users.get(toUser);
if(session == null)
return;
String msg = GsonUtil.object2JsonStr(message);
TextMessage textMsg = new TextMessage(msg);
try {
session.sendMessage(textMsg);
} catch (IOException e) {
log.error("XLTX-ERROR: websocket傳送訊息出錯", e);
}
}

}


4. 編寫握手攔截器 HandshakeInterceptor, 主要是為了在連線前將當前使用者的資訊儲存起來,方便伺服器推送資訊

public class HandshakeInterceptor extends HttpSessionHandshakeInterceptor {
// 初次握手訪問前
@Override
public boolean beforeHandshake(ServerHttpRequest request, ServerHttpResponse response,
WebSocketHandler webSocketHandler, Map<String, Object> attributes)
throws Exception {

if (request instanceof ServletServerHttpRequest) {

Session httpSession = SecurityUtils.getSubject().getSession();

if(httpSession != null){
//使用userName區分WebSocketHandler,以便定向傳送訊息, 如果需要向指定的使用者傳送訊息則需要記錄使用者資訊
//因此這裡將使用者id繫結到websocket的session
UserVO userVO = (UserVO)httpSession.getAttribute(SysConstant.SESSION_USER_INFO);
if(userVO != null){
long uid = userVO.getId();
                String userName = String.valueOf(uid);
                if (userName==null) {
                    userName="default-system";
                }
    attributes.put("WEBSOCKET_USERNAME", userName);
}else
attributes.put("WEBSOCKET_USERNAME", httpSession.getId());
}
}
return super.beforeHandshake(request, response, webSocketHandler, attributes);
}


// 初次握手訪問後
@Override
public void afterHandshake(ServerHttpRequest request, ServerHttpResponse response,
WebSocketHandler wsHandler, Exception e) {


super.afterHandshake(request, response, wsHandler, e);
}

}


以上個步驟就完成了websocket的服務端配置,在需要推送資訊的類中注入訊息處理器,即可進行訊息推送。

前端的編寫可以參考:https://blog.csdn.net/jared_he2017/article/details/79886600