本文最后更新于 2025-03-27T01:25:37+08:00
WebSocket 诞生背景
早期,很多网站为了实现推送技术,所用的技术都是轮询 。轮询是指由浏览器每隔一段时间向服务器发出 HTTP 请求,然后服务器返回最新的数据给客户端。
常见的轮询方式分为短轮询与长轮询,它们的区别如下图所示:

- 短轮训:客户端定期发送请求,服务器立即响应(无论有无数据),请求频率固定,资源消耗较大。
- 长轮训:请求挂起等待数据,服务器有数据才响应,减少无效请求,实时性较好
这种传统的模式带来很明显的缺点,即浏览器需要不断的向服务器发出请求,然而 HTTP 请求与响应可能会包含较长的头部,其中真正有效的数据可能只是很小的一部分,所以这样会消耗很多带宽资源。
在这种情况下,HTML5 定义了 WebSocket 协议,能更好的节省服务器资源和带宽,并且能够更实时地进行通讯。
WebSocket 与 HTTP 和 HTTPS 使用相同的 TCP 端口,可以绕过大多数防火墙的限制。
默认情况下:
- WebSocket 协议使用 80 端口;
- 若运行在 TLS 之上时,默认使用 443 端口。
1.1 WebSocket 简介
WebSocket 使得客户端和服务器之间的数据交换变得更加简单,允许服务端主动向客户端推送数据。在 WebSocket API 中,浏览器和服务器只需要完成一次握手,两者之间就可以创建持久性的连接,并进行双向数据传输。
1.2 WebSocket 优点
普遍认为,WebSocket的优点有如下几点:
- 较少的控制开销:在连接创建后,服务器和客户端之间交换数据时,用于协议控制的数据包头部相对较小;
- 更强的实时性:由于协议是全双工的,所以服务器可以随时主动给客户端下发数据。相对于 HTTP 请求需要等待客户端发起请求服务端才能响应,延迟明显更少;
- 保持连接状态:与 HTTP 不同的是,WebSocket 需要先创建连接,这就使得其成为一种有状态的协议,之后通信时可以省略部分状态信息;
- 更好的二进制支持:WebSocket 定义了二进制帧,相对 HTTP,可以更轻松地处理二进制内容;
- 可以支持扩展:WebSocket 定义了扩展,用户可以扩展协议、实现部分自定义的子协议。
由于 WebSocket 拥有上述的优点,所以它被广泛地应用在即时通讯领域。
手写 WebSocket 服务器(Java+JavaScript)
2.1 Java 写服务端
在 pom.xml 文件中添加 WebSocket 所需的依赖
1 2 3 4 5 6 7 8
| <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-websocket</artifactId> </dependency> <dependency> <groupId>javax.websocket</groupId> <artifactId>javax.websocket-api</artifactId> </dependency>
|
配置 WebSocket
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52
| import jakarta.websocket.*; import jakarta.websocket.server.ServerEndpoint; import org.springframework.stereotype.Component;
import java.io.IOException; import java.util.concurrent.CopyOnWriteArraySet;
@Component @ServerEndpoint("/ws") public class MyWebSocketServer {
private static final CopyOnWriteArraySet<MyWebSocketServer> webSocketSet = new CopyOnWriteArraySet<>(); private Session session;
@OnOpen public void onOpen(Session session) { this.session = session; webSocketSet.add(this); System.out.println("新的连接加入,当前在线人数:" + webSocketSet.size()); }
@OnMessage public void onMessage(String message, Session session) { System.out.println("收到消息:" + message); for (MyWebSocketServer webSocket : webSocketSet) { try { webSocket.sendMessage("广播消息:" + message); } catch (IOException e) { e.printStackTrace(); } } }
@OnClose public void onClose() { webSocketSet.remove(this); System.out.println("连接关闭,当前在线人数:" + webSocketSet.size()); }
@OnError public void onError(Session session, Throwable error) { System.err.println("发生错误:" + error.getMessage()); }
private void sendMessage(String message) throws IOException { this.session.getBasicRemote().sendText(message); } }
|
@ServerEndpoint 是一种轻量级的 WebSocket 实现方式,适合简单的实时通信应用场景。如果需要更多高级功能(如订阅、消息路由),可以结合 STOMP 协议和 Spring WebSocket 来实现复杂的实时通信系统。
配置 ServerEndpointExporter
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.web.socket.server.standard.ServerEndpointExporter;
@Configuration public class WebSocketConfig {
@Bean public ServerEndpointExporter serverEndpointExporter() { return new ServerEndpointExporter(); } }
|
2.2 JavaScript测试WebSocket
启动 Spring Boot 项目,服务会监听 /ws 路径。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58
| <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>WebSocket Client</title> </head> <body> <h1>WebSocket 客户端</h1> <textarea id="messages" cols="50" rows="10" readonly></textarea><br> <input type="text" id="inputMessage" placeholder="输入消息"> <button id="sendButton">发送</button>
<script> const messages = document.getElementById("messages"); const inputMessage = document.getElementById("inputMessage"); const sendButton = document.getElementById("sendButton");
// 创建 WebSocket 连接 const ws = new WebSocket("ws://localhost:8080/ws");
// 监听连接打开事件 ws.onopen = () => { messages.value += "连接已建立\n"; };
// 监听消息事件 ws.onmessage = (event) => { messages.value += "收到消息: " + event.data + "\n"; };
// 监听连接关闭事件, ws.onclose = () => { messages.value += "连接已关闭\n"; console.log("连接关闭,尝试重连..."); setTimeout(() => createWebSocket(url), 1000); // 1秒后重连 };
// 监听错误事件 ws.onerror = (error) => { messages.value += "发生错误\n"; console.error("WebSocket 错误:", error); };
// 点击按钮发送消息 sendButton.addEventListener("click", () => { const message = inputMessage.value; if (message && ws.readyState === WebSocket.OPEN) { ws.send(message); messages.value += "发送消息: " + message + "\n"; inputMessage.value = ""; } else { messages.value += "连接未打开或消息为空\n"; } }); </script> </body> </html>
|
WebSocket 与长轮询有什么区别
- 长轮询的本质基于 HTTP 协议,它仍然是一个一问一答(请求 — 响应)的模式。
- WebSocket 在握手成功后,就是全双工的 TCP 通道,数据可以主动从服务端发送到客户端。

WebSocket和Socket的区别
特性 |
Socket |
WebSocket |
所属层级 |
传输层 |
所属层级 |
通讯模式 |
点对点,全双工 |
点对点,全双工 |
协议支持 |
TCP/UDP |
自定义协议(基于Http/1.1或者更高) |
开发复杂度 |
高 |
低 |
使用场景 |
游戏,底层网络通讯协议 |
实时推送,聊天,在线协做工具 |
- 如果开发浏览器应用或实时性要求高的服务,WebSocket 是首选;如果需要完全控制通信流程和协议,选择 Socket。
参考资料