Skip to content

Commit 25e03e1

Browse files
committed
feat: add springboot-websocket-server-client
1 parent d892026 commit 25e03e1

File tree

7 files changed

+350
-0
lines changed

7 files changed

+350
-0
lines changed

pom.xml

+1
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@
4848
<module>springboot-shiro-jwt-redis</module>
4949
<module>springboot-mail-rabbitmq</module>
5050
<module>springboot-async</module>
51+
<module>springboot-websocket-server-client</module>
5152
</modules>
5253

5354
</project>
+53
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<project xmlns="http://maven.apache.org/POM/4.0.0"
3+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4+
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
5+
<modelVersion>4.0.0</modelVersion>
6+
7+
<artifactId>springboot-websocket-server-client</artifactId>
8+
9+
<parent>
10+
<groupId>org.springframework.boot</groupId>
11+
<artifactId>spring-boot-starter-parent</artifactId>
12+
<version>2.3.0.RELEASE</version>
13+
</parent>
14+
15+
<properties>
16+
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
17+
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
18+
<java.version>1.8</java.version>
19+
</properties>
20+
21+
<dependencies>
22+
<dependency>
23+
<groupId>org.springframework.boot</groupId>
24+
<artifactId>spring-boot-starter-web</artifactId>
25+
</dependency>
26+
<dependency>
27+
<groupId>org.springframework.boot</groupId>
28+
<artifactId>spring-boot-starter-websocket</artifactId>
29+
</dependency>
30+
31+
<dependency>
32+
<groupId>org.projectlombok</groupId>
33+
<artifactId>lombok</artifactId>
34+
<version>1.18.12</version>
35+
</dependency>
36+
37+
<!-- Other dependencies -->
38+
<dependency>
39+
<groupId>org.java-websocket</groupId>
40+
<artifactId>Java-WebSocket</artifactId>
41+
<version>1.5.2</version> <!-- 请根据需要选择适当的版本 -->
42+
</dependency>
43+
</dependencies>
44+
45+
<build>
46+
<plugins>
47+
<plugin>
48+
<groupId>org.springframework.boot</groupId>
49+
<artifactId>spring-boot-maven-plugin</artifactId>
50+
</plugin>
51+
</plugins>
52+
</build>
53+
</project>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
package cn.ruiyeclub;
2+
3+
import org.springframework.boot.SpringApplication;
4+
import org.springframework.boot.autoconfigure.SpringBootApplication;
5+
6+
/**
7+
* @author Cr.
8+
* @date 2024/5/30
9+
*/
10+
@SpringBootApplication
11+
public class Application {
12+
public static void main(String[] args) {
13+
SpringApplication.run(Application.class, args);
14+
}
15+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
package cn.ruiyeclub.config;
2+
3+
import org.springframework.beans.factory.annotation.Autowired;
4+
import org.springframework.context.annotation.Configuration;
5+
import org.springframework.web.servlet.config.annotation.CorsRegistry;
6+
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
7+
import org.springframework.web.socket.WebSocketHandler;
8+
import org.springframework.web.socket.config.annotation.EnableWebSocket;
9+
import org.springframework.web.socket.config.annotation.WebSocketConfigurer;
10+
import org.springframework.web.socket.config.annotation.WebSocketHandlerRegistry;
11+
12+
@Configuration
13+
@EnableWebSocket
14+
public class WebSocketServerConfig implements WebSocketConfigurer, WebMvcConfigurer {
15+
16+
@Autowired
17+
private WebSocketHandler webSocketHandler;
18+
19+
/**
20+
* WebSocket 是基于同源策略的,这意味着浏览器要求 WebSocket 连接的来源(协议、域名和端口)必须与页面来源相同。
21+
* 所以需要设置.setAllowedOrigins("*") 并且配置跨域
22+
*
23+
* @param registry
24+
*/
25+
@Override
26+
public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
27+
// 使用chat.html打开下面注释
28+
// registry.addHandler(webSocketHandler, "/connect").withSockJS(); // .withSockJS()只能用sockjs连接
29+
// 使用chat_v2.html打开下面
30+
registry.addHandler(webSocketHandler, "/connect")
31+
.setAllowedOrigins("*");
32+
33+
}
34+
35+
@Override
36+
public void addCorsMappings(CorsRegistry registry) {
37+
registry.addMapping("/connect/**")
38+
.allowedOrigins("*")
39+
.allowedMethods("*")
40+
.allowedHeaders("*")
41+
.allowCredentials(false);
42+
}
43+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
package cn.ruiyeclub.socket;
2+
3+
import lombok.extern.slf4j.Slf4j;
4+
import org.java_websocket.handshake.ServerHandshake;
5+
import org.springframework.stereotype.Component;
6+
import org.springframework.web.socket.TextMessage;
7+
import org.springframework.web.socket.WebSocketSession;
8+
9+
import java.io.IOException;
10+
import java.net.URI;
11+
import java.net.URISyntaxException;
12+
import java.util.concurrent.ConcurrentHashMap;
13+
14+
@Slf4j
15+
@Component
16+
public class WebSocketClient {
17+
18+
/**
19+
* 持久化客户端
20+
*/
21+
private static ConcurrentHashMap<String, org.java_websocket.client.WebSocketClient> websockets = new ConcurrentHashMap<>();
22+
/**
23+
* socket实例化
24+
*/
25+
private org.java_websocket.client.WebSocketClient webSocketClient;
26+
27+
28+
public void send(String msg, WebSocketSession session) {
29+
String id = session.getId();
30+
// System.out.println(id);
31+
org.java_websocket.client.WebSocketClient webSocketClient1 = websockets.get(id);
32+
if (webSocketClient1 == null) {
33+
connect(session);
34+
} else {
35+
log.info("二次连接");
36+
webSocketClient = webSocketClient1;
37+
this.sendMessage(msg);
38+
}
39+
// sendMessage(msg);
40+
}
41+
42+
43+
public void connect(WebSocketSession session) {
44+
try {
45+
URI uri = new URI("wss://streaming.forexpros.com/echo/687/dwo1lpgc/websocket");
46+
webSocketClient = new org.java_websocket.client.WebSocketClient(uri) {
47+
@Override
48+
public void onOpen(ServerHandshake handshake) {
49+
log.info("Connected to WebSocket server");
50+
}
51+
52+
@Override
53+
public void onMessage(String message) {
54+
log.info("Received message: " + message);
55+
// 在这里处理接收到的消息
56+
try {
57+
session.sendMessage(new TextMessage(message));
58+
} catch (IOException e) {
59+
throw new RuntimeException(e);
60+
}
61+
}
62+
63+
@Override
64+
public void onClose(int code, String reason, boolean remote) {
65+
log.info("Disconnected from WebSocket server with exit code " + code + " additional info: " + reason);
66+
}
67+
68+
@Override
69+
public void onError(Exception ex) {
70+
log.error("An error occurred:", ex);
71+
}
72+
};
73+
webSocketClient.connect();
74+
websockets.put(session.getId(), webSocketClient);
75+
log.info("当前用户id: {}", session.getId());
76+
log.info("当前客户端人数: {}", websockets.size());
77+
78+
// 发送请求给服务端
79+
// while (true) {
80+
// Thread.sleep(3000);
81+
// // 自定义事件`push_data_event` -> 向服务端发送消息
82+
// sendMessage("[\"{ \\\"_event\\\": \\\"heartbeat\\\", \\\"data\\\": \\\"h\\\"}\"]");
83+
// }
84+
// sendMessage(msg);
85+
} catch (URISyntaxException e) {
86+
log.error("Error connecting to WebSocket server", e);
87+
}
88+
}
89+
90+
public void disconnect() {
91+
if (webSocketClient != null) {
92+
webSocketClient.close();
93+
}
94+
}
95+
96+
public void sendMessage(String message) {
97+
if (webSocketClient != null && webSocketClient.isOpen()) {
98+
webSocketClient.send(message);
99+
} else {
100+
log.warn("WebSocket is not connected.");
101+
}
102+
}
103+
104+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
package cn.ruiyeclub.socket;
2+
3+
import lombok.extern.slf4j.Slf4j;
4+
import org.springframework.beans.factory.annotation.Autowired;
5+
import org.springframework.stereotype.Component;
6+
import org.springframework.web.socket.CloseStatus;
7+
import org.springframework.web.socket.TextMessage;
8+
import org.springframework.web.socket.WebSocketSession;
9+
import org.springframework.web.socket.handler.TextWebSocketHandler;
10+
11+
@Component
12+
@Slf4j
13+
//@ServerEndpoint("/connect")
14+
public class WebSocketHandler extends TextWebSocketHandler {
15+
16+
@Autowired
17+
WebSocketClient webSocketClient;
18+
19+
@Override
20+
public void afterConnectionEstablished(WebSocketSession session) {
21+
webSocketClient.connect(session);
22+
log.info("和客户端建立连接");
23+
}
24+
25+
@Override
26+
public void handleTransportError(WebSocketSession session, Throwable exception) throws Exception {
27+
session.close(CloseStatus.SERVER_ERROR);
28+
log.error("连接异常", exception);
29+
}
30+
31+
@Override
32+
public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {
33+
super.afterConnectionClosed(session, status);
34+
log.info("和客户端断开连接");
35+
}
36+
37+
@Override
38+
protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {
39+
// 获取到客户端发送过来的消息
40+
String receiveMessage = message.getPayload();
41+
log.info(receiveMessage);
42+
// 发送消息给客户端
43+
// session.sendMessage(new TextMessage(fakeAi(receiveMessage)));
44+
// 关闭连接
45+
// session.close(CloseStatus.NORMAL);
46+
webSocketClient.send(receiveMessage, session);
47+
}
48+
49+
private static String fakeAi(String input) {
50+
if (input == null || input.isEmpty()) {
51+
return "你说什么?没听清︎";
52+
}
53+
return input.replace('你', '我')
54+
.replace("吗", "")
55+
.replace('?', '!')
56+
.replace('?', '!');
57+
}
58+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
<!DOCTYPE html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="UTF-8">
5+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
6+
<title>WebSocket Client</title>
7+
<style>
8+
body {
9+
font-family: Arial, sans-serif;
10+
margin: 20px;
11+
}
12+
#messages {
13+
margin-top: 20px;
14+
border: 1px solid #ccc;
15+
padding: 10px;
16+
height: 200px;
17+
overflow-y: auto;
18+
}
19+
#messageInput {
20+
width: calc(100% - 22px);
21+
padding: 10px;
22+
margin-top: 10px;
23+
}
24+
#sendButton {
25+
padding: 10px;
26+
margin-top: 10px;
27+
cursor: pointer;
28+
}
29+
</style>
30+
</head>
31+
<body>
32+
<h1>WebSocket Client</h1>
33+
<div>
34+
<label for="messageInput">Message:</label>
35+
<input type="text" id="messageInput" placeholder="Enter your message here">
36+
<button id="sendButton">Send</button>
37+
</div>
38+
<div id="messages">
39+
<h3>Messages:</h3>
40+
</div>
41+
42+
<script>
43+
const wsUri = "ws://localhost:8080/connect";
44+
const websocket = new WebSocket(wsUri);
45+
46+
websocket.onopen = function(event) {
47+
displayMessage("Connected to WebSocket server");
48+
};
49+
50+
websocket.onmessage = function(event) {
51+
displayMessage("Received: " + event.data);
52+
};
53+
54+
websocket.onclose = function(event) {
55+
displayMessage("Disconnected from WebSocket server");
56+
};
57+
58+
websocket.onerror = function(event) {
59+
displayMessage("Error: " + event.data);
60+
};
61+
62+
document.getElementById("sendButton").onclick = function() {
63+
const message = document.getElementById("messageInput").value;
64+
websocket.send(message);
65+
displayMessage("Sent: " + message);
66+
};
67+
68+
function displayMessage(message) {
69+
const messagesDiv = document.getElementById("messages");
70+
const newMessage = document.createElement("div");
71+
newMessage.textContent = message;
72+
messagesDiv.appendChild(newMessage);
73+
}
74+
</script>
75+
</body>
76+
</html>

0 commit comments

Comments
 (0)