文章目录
- WebSocket是什么?
- 为什么需要WebSocket?
- WebSocket和Http连接的区别
- WebSocket的工作原理
- 基本交互过程:
- Java中的WebSocket支持
- WebSocket的优势
- springboot + websocket + themlef 一个聊天室demo
- pom.xml
- WebSocketConfig
- ChatController
- WebController
- chat.html
- application.yml
- 测试效果
WebSocket是什么?
WebSocket是一种基于TCP协议的双向通信协议,它允许服务器和客户端之间建立持久的连接。与传统的HTTP请求/响应模型不同,WebSocket连接一旦建立,可以在不断开的情况下双向传输数据。这使得WebSocket在实时应用中非常有用,例如在线游戏、即时聊天、实时协作工具等。
为什么需要WebSocket?
尽管HTTP协议广泛应用于网络通信,但它存在一个关键的局限性:通信只能由客户端发起。这种方式在需要频繁的数据更新和实时交互的场景中效率低下。例如,在一个聊天应用中,使用HTTP协议,客户端需要不断地发送请求以检查新消息,这种“轮询”机制浪费了大量的资源。WebSocket应运而生,解决了这个问题,通过建立一个持久的连接,实现了更高效的数据交换。
WebSocket和Http连接的区别
-
通信方式:
HTTP: HTTP是一种无状态协议,每次请求都是独立的,即使是在同一个客户端和服务器之间的多次请求。客户端向服务器发送请求,服务器处理请求并返回响应,然后连接关闭。HTTP通常是一种请求/响应协议。
WebSocket: WebSocket是一种全双工协议,允许双方建立持久性的连接,以便在连接建立后双向传输数据。WebSocket支持实时通信,允许服务器和客户端随时发送数据,而无需为每个消息建立新的连接。 -
连接建立:
HTTP: HTTP连接是临时的,每个请求都需要建立新的TCP连接,完成后即刻关闭。这导致了额外的延迟,尤其是在需要频繁通信的实时应用中。
WebSocket: WebSocket连接是持久的,一旦建立,它可以一直保持开放状态,不需要在每次通信之间重新建立连接。这减少了延迟,并使它适用于实时应用。
-
头部信息:
HTTP: HTTP请求和响应通常包括大量的头部信息,用于描述请求的属性、内容类型等。这些头部信息会占用额外的带宽,尤其是在小数据传输中显得不必要。
WebSocket: WebSocket头部信息相对较少,因为它专注于数据传输,减少了不必要的开销。 -
用途:
HTTP: HTTP主要用于请求和获取资源,如网页、图像、文档等。每个HTTP请求都是短暂的,没有保持连接的需求。
WebSocket: WebSocket适用于需要实时通信、双向数据交换的应用,如在线游戏、实时聊天、实时协作工具、监控系统等。 -
安全性:
HTTP: HTTP本身不提供加密,因此数据可以被拦截和窥探。但HTTPS通过TLS/SSL协议提供了数据加密和安全性。
WebSocket: WebSocket也可以通过WSS(WebSocket Secure)协议提供加密和安全性,以确保数据的机密性。
WebSocket的工作原理
基本交互过程:
1. 握手阶段: 客户端通过发送HTTP升级请求到服务器来启动WebSocket连接。服务器收到这个请求后,如果支持WebSocket,它将回复一个HTTP 101切换协议的响应,从而建立WebSocket连接。
2. 数据传输: 一旦WebSocket连接建立,客户端和服务器可以双向传输数据。无论是客户端还是服务器,都可以随时向对方发送消息。这种双向通信的实时性使WebSocket在很多应用场景中非常有用。
3. 关闭连接: 当需要关闭WebSocket连接时,任一方可以发送一个关闭帧,通知对方关闭连接。这确保了连接的正常关闭,释放资源。
Java中的WebSocket支持
Java提供了多种库和框架来支持WebSocket技术,其中最知名的是Java API for WebSocket(JSR-356),它是Java EE 7规范的一部分。Java WebSocket API允许你在Java应用程序中创建WebSocket服务器和客户端。
WebSocket的优势
减少开销: 在建立连接后,WebSocket不需要像HTTP那样为每个消息发送额外的头信息,这降低了通信的总体开销。
实时性: 全双工通信使得消息可以即时发送和接收,非常适合需要实时数据交互的应用。
节省资源: 相比于HTTP轮询,WebSocket通过维持持久连接减少了频繁建立和断开连接的资源消耗。
springboot + websocket + themlef 一个聊天室demo
pom.xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
WebSocketConfig
@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {
@Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/chat").withSockJS();
}
@Override
public void configureMessageBroker(MessageBrokerRegistry registry) {
registry.enableSimpleBroker("/topic");
registry.setApplicationDestinationPrefixes("/app");
}
}
ChatController
@Controller
public class ChatController {
@MessageMapping("/sendMessage")
@SendTo("/topic/public")
public String sendMessage(String message) {
return message;
}
}
WebController
@Controller
public class WebController {
@GetMapping("/chat")
public String chat() {
return "chat"; // Thymeleaf 模板文件的名称(不包含 .html 扩展名)
}
}
chat.html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<title>Chat Room</title>
<script src="https://cdn.jsdelivr.net/npm/sockjs-client/dist/sockjs.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/stomp-websocket/lib/stomp.min.js"></script>
<style>
#messageArea {
height: 300px;
overflow-y: auto;
border: 1px solid #cccccc;
padding: 10px;
margin-bottom: 10px;
}
.chat-message {
margin-bottom: 10px;
border: 1px solid #dddddd;
padding: 5px;
border-radius: 5px;
}
</style>
</head>
<body>
<div id="chatPage">
<div id="messageArea"></div>
<input type="text" id="messageInput" placeholder="Type a message...">
<button onclick="sendMessage()">Send</button>
</div>
<script th:inline="javascript">
var stompClient = null;
function connect() {
var socket = new SockJS('/chat');
stompClient = Stomp.over(socket);
stompClient.connect({}, function (frame) {
stompClient.subscribe('/topic/public', function (message) {
showMessage(message.body);
});
});
}
function sendMessage() {
var message = document.getElementById('messageInput').value;
stompClient.send("/app/sendMessage", {}, message);
document.getElementById('messageInput').value = '';
}
function showMessage(message) {
var messageArea = document.getElementById('messageArea');
var messageElement = document.createElement('div');
messageElement.classList.add('chat-message');
messageElement.innerText = message;
messageArea.appendChild(messageElement);
}
connect();
</script>
</body>
</html>
application.yml
server:
port: 8081
spring:
thymeleaf:
mode: HTML
cache: true
prefix: classpath:/templates/
encoding: UTF-8
suffix: .html
check-template-location: true
template-resolver-order: 1
测试效果
在浏览器访问http://localhost:8081/chat,打开多个界面
在其中一个窗口发送信息
其他窗口都收到了信息