WebSocket是一种在单个TCP连接上进行全双工通信的协议。它允许在浏览器和服务器之间建立实时的、双向的通信,从而使得实时的Web应用程序成为可能。
主要用途:实现实时的Web应用程序,例如在线游戏、聊天应用程序、股票市场行情等等。
(传统的HTTP协议是一种请求-响应协议,客户端需要不断地向服务器发送请求,服务器才能返回响应。这种方式在实现实时通信时效率很低,因为客户端需要不断地发送请求,而服务器也需要不断地返回响应。而WebSocket协议可以在客户端和服务器之间建立一条持久的连接,从而实现实时通信,避免了频繁的请求和响应,提高了通信效率。)
实时性:WebSocket可以实时地传输数据,不需要等待请求和响应的过程。
双向通信:WebSocket支持双向通信,客户端和服务器可以同时发送和接收数据。
轻量级:WebSocket协议比传统的HTTP协议更轻量级,可以减少通信的开销。
安全性:WebSocket支持加密传输,可以保证数据的安全性。
兼容性:WebSocket协议在一些旧浏览器中不被支持,需要使用polyfill或者fallback方案来兼容旧浏览器。
资源占用:WebSocket协议需要建立一条持久连接,会占用一定的服务器资源,需要在设计应用程序时考虑并发性的问题,以保证服务器的稳定性和性能。
安全性:WebSocket协议支持加密传输,但是需要在服务器端进行配置和实现,否则会存在安全隐患。
心跳机制:由于WebSocket连接是长连接,需要使用心跳机制来保持连接的稳定性。
断线重连:由于网络环境的不稳定性,WebSocket连接可能会断开,需要使用断线重连机制来保证连接的稳定性。
跨域:由于WebSocket协议需要在服务器端进行实现,因此可能会存在跨域问题。需要在服务器端进行相关配置,以允许跨域访问。
数据格式:WebSocket协议传输的数据格式可以是文本或二进制数据,可以传输图片、音频、视频等多媒体文件,需要在应用程序中进行相应的解析和处理。
使用ServerEndpoint方式实现WebSocket服务端的步骤:
org.springframework.boot
spring-boot-starter-websocket
@Configuration
public class WebSocketConfig {
@Bean
public ServerEndpointExporter serverEndpointExporter(){
return new ServerEndpointExporter();
}
}
创建一个Java类,并在类上添加@ServerEndpoint注解,指定WebSocket服务端的访问路径。
在类中定义一个或多个方法,用于处理WebSocket客户端的连接、消息和关闭事件。可以使用@OnOpen、@OnMessage、@OnClose等注解来标记这些方法,以便WebSocket容器自动调用它们。
在方法中,可以使用Session对象表示WebSocket客户端的会话,通过调用Session的getBasicRemote()方法获取到一个RemoteEndpoint.Basic对象,然后使用它的sendText()、sendBinary()等方法向客户端发送消息。
下面是一个简单的示例代码,演示了如何使用ServerEndpoint方式实现一个WebSocket服务端:
@ServerEndpoint("/websocket")
public class MyWebSocketServer {
@OnOpen
public void onOpen(Session session) {
System.out.println("WebSocket opened: " + session.getId());
}
@OnMessage
public void onMessage(Session session, String message) {
System.out.println("Received message: " + message);
try {
session.getBasicRemote().sendText("Server received message: " + message);
} catch (IOException e) {
e.printStackTrace();
}
}
@OnClose
public void onClose(Session session) {
System.out.println("WebSocket closed: " + session.getId());
}
}
类封装websocket的前端js实现:
class WebSocketClient {
constructor(url) {
this.url = url;
this.ws = null;
}
connect() {
this.ws = new WebSocket(this.url);
this.ws.onopen = () => {
console.log('WebSocket connected!');
};
this.ws.onmessage = (event) => {
console.log(`Received message: ${event.data}`);
};
this.ws.onclose = () => {
console.log('WebSocket closed!');
};
this.ws.onerror = (error) => {
console.error(`WebSocket error: ${error}`);
};
}
send(message) {
if (this.ws && this.ws.readyState === WebSocket.OPEN) {
this.ws.send(message);
} else {
console.error('WebSocket is not open!');
}
}
close() {
if (this.ws) {
this.ws.close();
}
}
}
// 使用示例
const ws = new WebSocketClient('ws://localhost:8080');
ws.connect();
ws.send('Hello, WebSocket!');
心跳:客户端和服务器之间定时发送的一些数据包,用于保持连接状态。由于WebSocket是基于TCP协议的,TCP协议本身是没有心跳机制的,因此需要应用层自己实现心跳机制,通过定时发送心跳包,可以防止因为长时间没有通信而导致的连接断开。
断线重连:在WebSocket连接断开后,客户端自动尝试重新连接服务器的过程。由于网络环境不稳定,很容易出现连接断开的情况,如果没有断线重连机制,就需要手动重新连接,非常不方便。通过断线重连机制,可以让客户端自动尝试重新连接服务器,提高应用的稳定性和可靠性。
setInterval(function() {
if (websocket.readyState === WebSocket.OPEN) {
websocket.send('heartbeat');
}
}, 30000); // 每30秒发送一次心跳包
function connect() {
websocket = new WebSocket('ws://example.com');
websocket.onopen = function(event) {
console.log('WebSocket连接成功');
};
websocket.onclose = function(event) {
console.log('WebSocket连接断开');
setTimeout(function() {
connect(); // 重新连接服务器
}, 5000); // 5秒后重新连接
};
websocket.onmessage = function(event) {
console.log('收到消息:' + event.data);
};
}
connect(); // 第一次连接服务器
import java.io.IOException;
import java.util.concurrent.ConcurrentHashMap;
import javax.websocket.*;
import javax.websocket.server.ServerEndpoint;
//通过@ServerEndpoint注解将WebSocketEndpoint类标记为WebSocket端点。
@ServerEndpoint("/websocket")
public class WebSocketEndpoint {
private static ConcurrentHashMap sessions = new ConcurrentHashMap<>();
private final int HEARTBEAT_INTERVAL = 30000; // 心跳包发送间隔,单位毫秒
private final int MAX_IDLE_TIME = 60000; // 最大空闲时间,单位毫秒
//在onOpen()方法中,我们创建了一个WebSocketSession对象,将其加入到sessions集合中,并启动心跳包。
@OnOpen
public void onOpen(Session session) {
WebSocketSession websocketSession = new WebSocketSession(session);
sessions.put(session.getId(), websocketSession);
startHeartbeat(websocketSession); // 连接成功后启动心跳包
}
//在onClose()方法中,我们从sessions集合中移除WebSocketSession对象。
@OnClose
public void onClose(Session session) {
WebSocketSession websocketSession = sessions.get(session.getId());
if (websocketSession != null) {
websocketSession.close();
sessions.remove(session.getId());
}
}
//在onError()方法中,我们处理传输错误。
@OnError
public void onError(Session session, Throwable error) {
System.out.println("WebSocket传输错误");
}
//在onMessage()方法中,我们处理接收到的消息,如果是心跳包则更新最后活跃时间。
@OnMessage
public void onMessage(Session session, String message) {
WebSocketSession websocketSession = sessions.get(session.getId());
if (message.equals("heartbeat")) {
System.out.println("收到心跳包");
websocketSession.updateLastActiveTime();
} else {
System.out.println("收到消息:" + message);
}
}
//在startHeartbeat()方法中,我们使用一个新的线程来定时发送心跳包,如果WebSocket连接已经关闭,则退出循环。
private void startHeartbeat(WebSocketSession websocketSession) {
Runnable task = new Runnable() {
@Override
public void run() {
while (true) {
try {
Thread.sleep(HEARTBEAT_INTERVAL);
if (websocketSession.isOpen()) {
websocketSession.send("heartbeat");
} else {
break;
}
} catch (Exception e) {
e.printStackTrace();
break;
}
}
}
};
new Thread(task).start();
}
//在WebSocketSession内部类中,封WebSocket会话的基本操作,并实现了更新最后活跃时间和判断是否超时的方法。
private static class WebSocketSession {
private Session session;
private Long lastActiveTime;
public WebSocketSession(Session session) {
this.session = session;
this.lastActiveTime = System.currentTimeMillis();
}
public void send(String message) {
try {
session.getBasicRemote().sendText(message);
} catch (IOException e) {
e.printStackTrace();
}
}
public void close() {
try {
session.close();
} catch (IOException e) {
e.printStackTrace();
}
}
public boolean isOpen() {
return session.isOpen();
}
public void updateLastActiveTime() {
this.lastActiveTime = System.currentTimeMillis();
}
public boolean isIdleTimeout() {
return (System.currentTimeMillis() - lastActiveTime) > MAX_IDLE_TIME;
}
}
//遍历sessions集合,判断每个WebSocketSession对象是否已经超时,如果超时则关闭WebSocket连接并从sessions集合中移除。可以通过定时任务或者Servlet过滤器定期调用WebSocketEndpoint.checkIdleTimeout()方法,实现WebSocket的断线重连。
public static void checkIdleTimeout() {
for (WebSocketSession websocketSession : sessions.values()) {
if (websocketSession.isIdleTimeout()) {
websocketSession.close();
sessions.remove(websocketSession.session.getId());
}
}
}
}