先推荐大家一个前端socket在线工具 https://www.idcd.com/tool/socket
一、前端实验代码
<!--仅供测试-->
<!-- websocket的配置 -->
<script>
var path = 'localhost:89/xxxx';
var ws;
var lockReconnect = false;
var ipRanIp=Math.random()*10000;
var wsUrl = getwsurl();
createWebSocket(wsUrl);
function getwsurl() {
if ('WebSocket' in window) {
return "ws://" + path + "/ws.action?uid=1&uname=2&clientIp=3";
} else if ('MozWebSocket' in window) {
return "ws://" + path + "/ws" + uid;
} else {
return "http://" + path + "/ws/sockjs" + uid;
}
}
function createWebSocket(url) {
try {
ws = new WebSocket(url);
initEventHandle();
} catch (e) {
reconnect(url);
}
}
function initEventHandle() {
ws.onclose = function () {
console.info("连接关闭");
};
ws.onerror = function () {
console.info("传输异常");
reconnect(wsUrl);
};
ws.onopen = function () {
heartCheck.reset().start();
};
ws.onmessage = function (event) {
console.info(event.data);
heartCheck.reset().start();
}
}
function reconnect(url) {
if(lockReconnect) return;
lockReconnect = true;
setTimeout(function () {
console.info("尝试重连..." + new Date().format("yyyy-MM-dd hh:mm:ss"));
createWebSocket(url);
lockReconnect = false;
}, 5000);
}
var heartCheck = {
timeout: 20000,
timeoutObj: null,
serverTimeoutObj: null,
reset: function(){
clearTimeout(this.timeoutObj);
clearTimeout(this.serverTimeoutObj);
return this;
},
start: function(){
var self = this;
this.timeoutObj = setTimeout(function(){
ws.send("HeartBeat" + new Date().format("yyyy-MM-dd hh:mm:ss"));
console.info("客户端发送心跳:" + new Date().format("yyyy-MM-dd hh:mm:ss"));
self.serverTimeoutObj = setTimeout(function(){
ws.close();
}, self.timeout)
}, this.timeout)
}
}
Date.prototype.format = function(fmt) {
var o = {
"M+" : this.getMonth()+1,
"d+" : this.getDate(),
"h+" : this.getHours(),
"m+" : this.getMinutes(),
"s+" : this.getSeconds(),
"q+" : Math.floor((this.getMonth()+3)/3),
"S" : this.getMilliseconds()
};
if(/(y+)/.test(fmt)) {
fmt=fmt.replace(RegExp.$1, (this.getFullYear()+"").substr(4 - RegExp.$1.length));
}
for(var k in o) {
if(new RegExp("("+ k +")").test(fmt)){
fmt = fmt.replace(RegExp.$1, (RegExp.$1.length==1) ? (o[k]) : (("00"+ o[k]).substr((""+ o[k]).length)));
}
}
return fmt;
}
</script>
二、服务端
1、先说maven依赖
(由于我的项目 spring mvc 版本也是 4.3.29.RELEASE ,所以这里我选取 4.3.29.RELEASE 的版本)
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-websocket</artifactId>
<version>4.3.29.RELEASE</version>
</dependency>
2、线程池 (可选) :
package cn.dbsec.task;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import java.util.concurrent.ThreadPoolExecutor;
@Configuration
@EnableAsync
public class BeanConfig {
private int corePoolSize=2;
private int maximumPoolSize=2;
private int queueMax=2000;
private int keepAliveTime=6;
@Bean
public ThreadPoolTaskExecutor taskExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(corePoolSize);
executor.setMaxPoolSize(maximumPoolSize);
executor.setQueueCapacity(queueMax);
executor.setKeepAliveSeconds(keepAliveTime);
executor.setThreadNamePrefix("socket_thread-");
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
executor.setWaitForTasksToCompleteOnShutdown(true);
return executor;
}
}
3、拦截器实现 HandShakeInterceptor.cladd
package cn.dbsec.system.websocket;
import java.util.Map;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.web.socket.WebSocketHandler;
import org.springframework.web.socket.server.HandshakeInterceptor;
public class HandShakeInterceptor implements HandshakeInterceptor {
public boolean beforeHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler, Map<String, Object> attributes) throws Exception {
System.out.println("将要进入WebSocket预处理过程");
return true;
}
public void afterHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler, Exception exception) {
System.out.println("WebSocket预处理中....");
System.out.println("webSocket预处理完毕");
}
}
4、连接状态捕获 MyWebSocketHandler.class
package cn.dbsec.system.websocket;
import java.text.SimpleDateFormat;
import java.util.*;
import cn.dbsec.dbaa.pojo.system.WebProvingEntity;
import cn.dbsec.system.dao.WbProvingDao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.socket.CloseStatus;
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketHandler;
import org.springframework.web.socket.WebSocketMessage;
import org.springframework.web.socket.WebSocketSession;
@Component
public class MyWebSocketHandler implements WebSocketHandler {
private volatile static List<WebSocketSession> users = new ArrayList<>();
private static final SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
public void afterConnectionEstablished(WebSocketSession session) throws Exception {
System.out.println("进入真正的握手类:MyWebSocketHandler,WebSocket连接建立成功");
users.add(session);
System.out.println("在线用户" + users.size() + "人:" + users);
}
public void handleMessage(WebSocketSession session, WebSocketMessage<?> message) throws Exception {
System.out.println("接收到客户端:" + session.getId() + "发送的消息:" + message.getPayload().toString());
session.sendMessage(new TextMessage("服务器的心跳回应-HeartBeat" + sdf.format(new Date())));
}
public void handleTransportError(WebSocketSession session,Throwable exception) throws Exception {
users.remove(session);
System.out.println("客户端" + session.getId() + "传输异常");
}
public void afterConnectionClosed(WebSocketSession session,CloseStatus closeStatus) throws Exception {
users.remove(session);
session.close();
System.out.println("Websocket客户端" + session.getId() + "已经关闭");
}
public boolean supportsPartialMessages() {
return false;
}
private Map setParamToMap(String query){
Map<String, Object> mapRes = new HashMap<>();
String[] params = query.split("&");
Map paramMap = new HashMap<>();
for (String param : params) {
String[] keyValue = param.split("=");
mapRes.put(keyValue[0], keyValue[1]);
}
return mapRes;
}
}
5、对指定请求进行拦截 WebSocketConfig.class
这里要着重说明一下两个配置点:
1、由于websocket 请求其实也是 http 请求,所以该请求也是会经过我们 spring mvc 的 servlet 进行转发、捕获,所以这里需要看一下你捕获时候的规则配置,比如我的就是匹配 “ .action ” , 如图: 所以我们配置socket拦截路径时,也必须以 .action 结尾,不然前端调用时就会出现 404 : 2、客户端连接时的作用域问题: 这里我配置为所有,正常情况下,你需要配置为你客户端的 IP ,这样才能保证网络安全性。 源码:
package cn.dbsec.system.websocket;
import javax.annotation.Resource;
import org.springframework.stereotype.Component;
import org.springframework.web.socket.config.annotation.EnableWebSocket;
import org.springframework.web.socket.config.annotation.WebSocketConfigurer;
import org.springframework.web.socket.config.annotation.WebSocketHandlerRegistry;
@Component
@EnableWebSocket
public class WebSocketConfig implements WebSocketConfigurer{
@Resource
MyWebSocketHandler handler;
public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
registry.addHandler(handler, "/ws.action").addInterceptors(new HandShakeInterceptor()).setAllowedOrigins("*");
registry.addHandler(handler, "/ws/sockjs.action").addInterceptors(new HandShakeInterceptor()).withSockJS();
}
}
|