Springboot+WebSocket 自动重连机制
1、WebSocket
WebSocket 是一种在单个TCP连接上进行全双工通信的协议,通信协议可能更熟悉的是HTTP,因此,学习WebSocket可以-以- HTTP为参考点。
HTTP 协议的缺陷是通信只能由客户端发起,做不到服务器主动向客户端推送信息。
WebSocket 协议它的最大特点就是弥补了HTTP的缺陷,服务器可以主动向客户端推送信息,客户端也可以主动向服务器发送信息,是真正的双向平等对话,属于消息推送技术的一种。
需要注意的是:WebSocket 不能与Socket同论,因为Socket是一种接口而不是协议,即Socket是HTTP实现的一种接口。
因为WebSocket是一种协议,可以有很多种实现通信的方法,从不同编程语言到框架设计的不同。
以下将是配合Springboot以及一些工具包实现的一种案例,仅供参考。
2、案例
2.1、服务端
(项目初始化过程省略…)
2.1.1、依赖包
(相关版本号根据所处的年代自行更替)
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.76</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
<dependency>
<groupId>javax.websocket</groupId>
<artifactId>javax.websocket-api</artifactId>
<version>1.1</version>
</dependency>
<dependency>
<groupId>commons-lang</groupId>
<artifactId>commons-lang</artifactId>
<version>2.6</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-api</artifactId>
<version>2.13.3</version>
</dependency>
2.1.2、启动类
添加注解@EnableWebSocket
@SpringBootApplication
@EnableWebSocket
public class WSServerApplication {
public static void main(String[] args) {
SpringApplication.run(WSServerApplication .class, args);
}
}
2.1.3、配置类
使springboot扫描@ServerEndpoint注解
/**
* @Description: 配置类
*/
@Configuration
public class WebSocketConfig {
/**
* ServerEndpointExporter 作用
*
* 这个Bean会自动注册使用@ServerEndpoint注解声明的websocket endpoint
*
* @return
*/
@Bean
public ServerEndpointExporter serverEndpointExporter() {
return new ServerEndpointExporter();
}
}
2.1.4、工具类
管理连接集合
public class WebSocketMapUtil {
public static ConcurrentMap<String, MyWebSocketServer> webSocketMap = new ConcurrentHashMap<>();
public static void put(String key, MyWebSocketServer myWebSocketServer) {
webSocketMap.put(key, myWebSocketServer);
}
public static MyWebSocketServer get(String key) {
return webSocketMap.get(key);
}
public static void remove(String key) {
webSocketMap.remove(key);
}
public static Collection<MyWebSocketServer> getValues() {
return webSocketMap.values();
}
}
2.1.5、服务
@ServerEndpoint(value = "/myWebSocket")
@Component
public class MyWebSocketServer {
private Logger logger = LoggerFactory.getLogger(MyWebSocketServer.class);
private Session session;
/**
* * 连接建立后触发的方法
*/
@OnOpen
public void onOpen(Session session) {
this.session = session;
logger.info("onOpen" + session.getId());
WebSocketMapUtil.put(session.getId(), this);
}
/**
* * 连接关闭后触发的方法
*/
@OnClose
public void onClose() {
//从map中删除
WebSocketMapUtil.remove(session.getId());
logger.info("====== onClose:" + session.getId() + " ======");
}
/**
* * 接收到客户端消息时触发的方法
*/
@OnMessage
public void onMessage(String params, Session session) throws Exception {
//获取服务端到客户端的通道
MyWebSocketServer myWebSocket = WebSocketMapUtil.get(session.getId());
logger.info("收到来自" + session.getId() + "的消息" + params);
String result = "收到来自" + session.getId() + "的消息" + params;
//返回消息给WebSocket客户端
myWebSocket.sendMessage(1, "成功!", result);
}
/**
* * 发生错误时触发的方法
*/
@OnError
public void onError(Session session, Throwable error) {
logger.info(session.getId() + "连接发生错误" + error.getMessage());
error.printStackTrace();
}
public void sendMessage(int status, String message, Object datas) throws IOException {
JSONObject result = new JSONObject();
result.put("status", status);
result.put("message", message);
result.put("datas", datas);
this.session.getBasicRemote().sendText(result.toString());
}
}
2.2、客户端
(项目初始化过程省略…)
2.2.1、依赖包
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-api</artifactId>
<version>2.13.3</version>
</dependency>
<dependency>
<groupId>org.java-websocket</groupId>
<artifactId>Java-WebSocket</artifactId>
<version>1.3.8</version>
</dependency>
2.2.2、启动类
@SpringBootApplication
public class ClientApplication {
public static void main(String[] args) {
SpringApplication.run(ClientApplication .class, args);
}
}
2.2.3、客户端管理器
/**
* 客户端管理器
*/
public abstract class ClientManager {
private static Logger logger = LoggerFactory.getLogger(ClientManager.class);
//服务端IP
private String url;
//所管理的WebSocket
private MyWebsocketClient websocketClient = null;
//定时器
private Timer dogCheckTimer = null;
public ClientManager(String url) {
this.url = url;
}
/**
* 连接服务器
*/
public void connecToServer() {
this.createWebSocket();
this.timerCheckWebSocket(); //定期检查websocketClient状态,关闭则创建
}
private void createWebSocket() {
if (this.getWebsocketClient() != null) {
this.getWebsocketClient().close();
this.setWebsocketClient(null);
}
try {
MyWebsocketClient client = new MyWebsocketClient(new URI(url), new Draft_6455(), new HashMap<>(), 300);
this.setWebsocketClient(client);
this.websocketClient.setManager(this);
client.connectBlocking();
} catch (Exception e) {
logger.error("创建 websocket client 异常:{}", e);
}
}
/**
**重连机制 定时检查连接
*/
private void timerCheckWebSocket() {
if (this.dogCheckTimer == null)
this.dogCheckTimer = new Timer();
this.dogCheckTimer.scheduleAtFixedRate(new TimerTask() {
@Override
public void run() {
//websocketClient不为空
if (ClientManager.this.getWebsocketClient() != null) {
WebSocket.READYSTATE dogReadyState = ClientManager.this.getWebsocketClient().getReadyState();
if (!dogReadyState.equals(WebSocket.READYSTATE.OPEN))
try {
ClientManager.this.createWebSocket();
} catch (Exception e) {
logger.error("重新创建 websocket client 失败:{}", e);
}
}
}
}, 5000L, 5000L);
}
public abstract void onOpen();
public abstract void onMessage(String s);
public abstract void onClose(int i, String s, boolean b);
public abstract void onError(Exception e);
/**
* 发送字符串消息
*
* @param message
*/
public void send(String message) {
if (getWebsocketClient() != null)
getWebsocketClient().send(message);
}
// getter and setter
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
public Timer getDogCheckTimer() {
return dogCheckTimer;
}
public void setDogCheckTimer(Timer dogCheckTimer) {
this.dogCheckTimer = dogCheckTimer;
}
public synchronized MyWebsocketClient getWebsocketClient() {
return websocketClient;
}
public synchronized void setWebsocketClient(MyWebsocketClient websocketClient) {
this.websocketClient = websocketClient;
}
}
2.2.4、客户端
public class MyWebsocketClient extends WebSocketClient {
private Logger logger = LoggerFactory.getLogger(MyWebsocketClient.class);
private ClientManager manager;
public MyWebsocketClient(URI serverUri) {
super(serverUri);
}
public MyWebsocketClient(URI serverUri, Draft protocolDraft) {
super(serverUri, protocolDraft);
}
public MyWebsocketClient(URI serverUri, Draft protocolDraft, Map<String, String> httpHeaders, int connectTimeout) {
super(serverUri, protocolDraft, httpHeaders, connectTimeout);
}
@Override
public void onOpen(ServerHandshake serverHandshake) {
getManager().onOpen();
}
@Override
public void onMessage(String s) {
if (this.manager != null){
manager.onMessage(s);
}
}
@Override
public void onClose(int i, String s, boolean b) {
if (this.manager != null){
manager.onClose(i,s,b);
}
}
@Override
public void onError(Exception e) {
if (this.manager != null){
manager.onError(e);
}
}
public ClientManager getManager() {
return manager;
}
public void setManager(ClientManager manager) {
this.manager = manager;
}
}
2.2.5、客户端实例
这里才是真正应用到的客户端,前面的管理器以及客户端是抽象出的一层。
public class TestReceiver extends ClientManager implements Runnable{
private static Logger logger = LoggerFactory.getLogger(TestReceiver.class);
public TestReceiver(String url) {
super(url);
}
@Override
public void onOpen() {
logger.info("=======客户端连接成功========");
Scanner scanner = new Scanner(System.in);
while (true) {
System.out.println("请输入消息:");
this.send(scanner.next());
try {
//延迟一秒
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
@Override
public void onMessage(String s) {
logger.info("=======服务的返回的信息:====={}===",s);
}
@Override
public void onClose(int i, String s, boolean b) {
logger.info("=======客户端连接关闭成功========");
}
@Override
public void onError(Exception e) {
logger.error("=======客户端连接异常========");
}
@Override
public void run() {
logger.info("客户端启动");
this.connecToServer();
}
}
2.2.6、运行
@Component
public class webSocketInit {
private static Logger logger = LoggerFactory.getLogger(webSocketInit.class);
//服务器地址;websocket是ws://
private String uri = "ws://localhost:7000/myWebSocket";
@PostConstruct
public void init(){
new Thread(new TestReceiver(uri)).start();
}
}
3、效果
(当然这里的效果不是重点…,你可以套入各种对应的场景)
客户端自动重连。