一、 简介
1.1 什么是WebSocket
WebSocket是一种协议,用于在Web应用程序和服务器之间建立实时、双向的通信连接。它通过一个单一的TCP连接提供了持久化连接,这使得Web应用程序可以更加实时地传递数据。WebSocket协议最初由W3C开发,并于2011年成为标准。
1.2 WebSocket的优势和劣势
WebSocket的优势包括:
-
实时性:
由于WebSocket的持久化连接,它可以实现实时的数据传输,避免了Web应用程序需要不断地发送请求以获取最新数据的情况。
-
双向通信:
WebSocket协议支持双向通信,这意味着服务器可以主动向客户端发送数据,而不需要客户端发送请求。
-
减少网络负载:
由于WebSocket的持久化连接,它可以减少HTTP请求的数量,从而减少了网络负载。
WebSocket的劣势包括:
-
需要浏览器和服务器都支持:
WebSocket是一种相对新的技术,需要浏览器和服务器都支持。一些旧的浏览器和服务器可能不支持WebSocket。
-
需要额外的开销:
WebSocket需要在服务器上维护长时间的连接,这需要额外的开销,包括内存和CPU。
-
安全问题:
由于WebSocket允许服务器主动向客户端发送数据,可能会存在安全问题。服务器必须保证只向合法的客户端发送数据。
二、 WebSocket的基本概念
2.1 WebSocket的协议
WebSocket 协议是一种基于TCP的协议,用于在客户端和服务器之间建立持久连接,并且可以在这个连接上实时地交换数据。WebSocket协议有自己的握手协议,用于建立连接,也有自己的数据传输格式。
当客户端发送一个 WebSocket 请求时,服务器将发送一个协议响应以确认请求。在握手期间,客户端和服务器将协商使用的协议版本、支持的子协议、支持的扩展选项等。一旦握手完成,连接将保持打开状态,客户端和服务器就可以在连接上实时地传递数据。
WebSocket 协议使用的是双向数据传输,即客户端和服务器都可以在任意时间向对方发送数据,而不需要等待对方的请求。它支持二进制数据和文本数据,可以自由地在它们之间进行转换。
总之,WebSocket协议是一种可靠的、高效的、双向的、持久的通信协议,它适用于需要实时通信的Web应用程序,如在线游戏、实时聊天等。
2.2 WebSocket的生命周期
WebSocket 生命周期描述了 WebSocket 连接从创建到关闭的过程。一个 WebSocket 连接包含以下四个主要阶段:
-
连接建立阶段(Connection Establishment):
在这个阶段,客户端和服务器之间的 WebSocket 连接被建立。客户端发送一个 WebSocket 握手请求,服务器响应一个握手响应,然后连接就被建立了。
-
连接开放阶段(Connection Open):
在这个阶段,WebSocket 连接已经建立并开放,客户端和服务器可以在连接上互相发送数据。
-
连接关闭阶段(Connection Closing):
在这个阶段,一个 WebSocket 连接即将被关闭。它可以被客户端或服务器发起,通过发送一个关闭帧来关闭连接。
-
连接关闭完成阶段(Connection Closed):
在这个阶段,WebSocket 连接已经完全关闭。客户端和服务器之间的任何交互都将无效。
“
需要注意的是,WebSocket 连接在任何时候都可能关闭,例如网络故障、服务器崩溃等情况都可能导致连接关闭。因此,需要及时处理 WebSocket 连接关闭的事件,以确保应用程序的可靠性和稳定性。
下面是一个简单的 WebSocket 生命周期示意图:
在这个示意图中,客户端向服务器发送一个 WebSocket 握手请求,服务器响应一个握手响应,连接就被建立了。一旦连接建立,客户端和服务器就可以在连接上互相发送数据,直到其中一方发送一个关闭帧来关闭连接。在关闭帧被接收后,连接就会被关闭,WebSocket 连接关闭完成。
2.3 WebSocket的消息格式
WebSocket 的消息格式与 HTTP 请求和响应的消息格式有所不同。WebSocket 的消息格式可以是文本或二进制数据,并且 WebSocket 消息的传输是在一个已经建立的连接上进行的,因此不需要再进行 HTTP 请求和响应的握手操作。
WebSocket 消息格式由两个部分组成:消息头和消息体。
消息头包含以下信息:
-
FIN:
表示这是一条完整的消息,一般情况下都是1。
-
RSV1、RSV2、RSV3:
暂时没有使用,一般都是0。
-
Opcode:
表示消息的类型,包括文本消息、二进制消息等。
-
-
Payload length:
表示消息体的长度。
-
Masking key:
仅在消息需要加密时出现,用于对消息进行解密。
消息体就是实际传输的数据,可以是文本或二进制数据。
2.4 WebSocket的API
WebSocket API 是用于在 Web 应用程序中创建和管理 WebSocket 连接的接口集合。WebSocket API 由浏览器原生支持,无需使用额外的 JavaScript 库或框架,可以直接在 JavaScript 中使用。
下面是一些常用的 WebSocket API:
WebSocket 构造函数:
WebSocket 构造函数用于创建 WebSocket 对象。它接受一个 URL 作为参数,表示要连接的 WebSocket 服务器的地址。例如:
let ws = new WebSocket('ws://example.com/ws');
WebSocket.send() 方法:
WebSocket.send()
方法用于向服务器发送数据。它接受一个参数,表示要发送的数据。数据可以是字符串、Blob 对象或 ArrayBuffer 对象。例如:
ws.send('Hello, server!');
WebSocket.onopen 事件:
WebSocket.onopen
事件在 WebSocket 连接成功建立时触发。例如:
ws.onopen = function() {
console.log('WebSocket 连接已经建立。');
};
WebSocket.onmessage 事件:
WebSocket.onmessage
事件在接收到服务器发送的消息时触发。它的 event 对象包含一个 data 属性,表示接收到的数据。例如:
ws.onmessage = function(event) {
console.log('收到服务器消息:', event.data);
};
WebSocket.onerror 事件:
WebSocket.onerror
事件在 WebSocket 连接出现错误时触发。例如:
ws.onerror = function(event) {
console.error('WebSocket 连接出现错误:', event);
};
WebSocket.onclose 事件:
WebSocket.onclose
事件在 WebSocket 连接被关闭时触发。例如:
ws.onclose = function() {
console.log('WebSocket 连接已经关闭。');
};
以上是一些常用的 WebSocket API。
三、 在Java中使用WebSocket
依赖:
<dependency>
<groupId>javax.websocketgroupId>
<artifactId>javax.websocket-apiartifactId>
<version>1.1version>
dependency>
3.1 使用Java WebSocket API编写WebSocket服务端
下面是一个使用 Java WebSocket API 编写 WebSocket 服务端的示例代码:
import javax.websocket.*;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
@ServerEndpoint("/echo")
public class EchoServer {
@OnOpen
public void onOpen(Session session) {
System.out.println("WebSocket 连接已经建立。");
}
@OnMessage
public void onMessage(String message, Session session) throws IOException {
System.out.println("收到客户端消息:" + message);
session.getBasicRemote().sendText("服务器收到消息:" + message);
}
@OnClose
public void onClose() {
System.out.println("WebSocket 连接已经关闭。");
}
@OnError
public void onError(Throwable t) {
System.out.println("WebSocket 连接出现错误:" + t.getMessage());
}
}
这个示例代码定义了一个名为 "echo" 的 WebSocket 端点,它会监听客户端发来的消息,并将收到的消息返回给客户端。具体来说,它使用了
@ServerEndpoint
注解来指定 WebSocket 端点的 URL,使用了
@OnOpen
、
@OnMessage
、
@OnClose
和
@OnError
注解来定义 WebSocket 事件处理器。
要使用这个 WebSocket 服务端,我们需要部署它到一个支持 WebSocket 的 Web 容器中。例如,我们可以使用 Tomcat 8 或以上版本来运行它。在部署完成后,我们可以使用任何支持 WebSocket 的客户端来连接这个服务端,发送消息并接收服务器的响应。
例如,下面是一个简单的 HTML/JavaScript 客户端代码:
html>
<html>
<head>
<meta charset="UTF-8">
<title>WebSocket Demotitle>
<script>
var ws = new WebSocket('ws://localhost:8080/echo');
ws.onopen = function() {
console.log('WebSocket 连接已经建立。');
ws.send('Hello, server!');
};
ws.onmessage = function(event) {
console.log('收到服务器消息:', event.data);
};
ws.onerror = function(event) {
console.error('WebSocket 连接出现错误:', event);
};
ws.onclose = function() {
console.log('WebSocket 连接已经关闭。');
};
script>
head>
<body>
<h1>WebSocket Demoh1>
body>
html>
这个客户端使用了 WebSocket 构造函数来创建一个 WebSocket 对象,并指定连接的 URL 为我们之前部署的服务端的 URL。它使用了 WebSocket 的事件处理器来处理 WebSocket 事件,例如当 WebSocket 连接成功建立时,它会向服务器发送一条消息,并在收到服务器的响应时打印出消息内容。
3.2 使用Java WebSocket API编写WebSocket客户端
下面是一个使用 Java WebSocket API 编写 WebSocket 客户端的示例代码:
import javax.websocket.*;
import java.io.IOException;
import java.net.URI;
@ClientEndpoint
public class EchoClient {
private Session session;
@OnOpen
public void onOpen(Session session) {
System.out.println("WebSocket 连接已经建立。");
this.session = session;
}
@OnMessage
public void onMessage(String message, Session session) {
System.out.println("收到服务器消息:" + message);
}
@OnClose
public void onClose() {
System.out.println("WebSocket 连接已经关闭。");
}
@OnError
public void onError(Throwable t) {
System.out.println("WebSocket 连接出现错误:" + t.getMessage());
}
public void connect(String url) throws Exception {
WebSocketContainer container = ContainerProvider.getWebSocketContainer();
container.connectToServer(this, new URI(url));
}
public void send(String message) throws IOException {
session.getBasicRemote().sendText(message);
}
public void close() throws IOException {
session.close();
}
}
3.3 使用Spring Boot编写WebSocket服务端
创建Spring Boot项目
首先,您需要创建一个新的Spring Boot项目。可以使用Spring Initializr创建一个新项目,添加依赖项。
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-websocketartifactId>
dependency>
配置WebSocket
应用程序中,需要配置WebSocket。创建一个新的Java类,并添加注释
@ServerEndpoint("/websocket")
。这将指定WebSocket服务端的端点。
在此类中,需要实现几个方法:
import javax.websocket.OnClose;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.ServerEndpoint;
@ServerEndpoint("/websocket")
public class WebSocketServer {
@OnOpen
public void onOpen(Session session) {
System.out.println("Connection opened: " + session.getId());
sessions.add(session);
}
@OnMessage
public void onMessage(Session session, String message) throws IOException {
System.out.println("Received message: " + message);
session.getBasicRemote().sendText("Server received: " + message);
}
@OnClose
public void onClose(Session session) {
System.out.println("Connection closed: " + session.getId());
sessions.remove(session);
}
private static final Set sessions = Collections.synchronizedSet(new HashSet());
}
处理WebSocket消息
在
@OnMessage
方法中,可以处理WebSocket客户端发送的消息,并向客户端发送响应。下面是一个简单的示例代码:
@OnMessage
public void onMessage(Session session, String message) throws IOException {
System.out.println("Received message: " + message);
session.getBasicRemote().sendText("Server received: " + message);
}
在此代码中,我们简单地打印出收到的消息,并向客户端发送响应。
关闭WebSocket连接
在
@OnClose
方法中,可以删除连接并做一些清理工作。下面是一个示例代码:
@OnClose
public void onClose(Session session) {
System.out.println("Connection closed: " + session.getId());
sessions.remove(session);
}
在此代码中,我们从连接池中删除连接,并打印出连接已关闭的消息。
配置WebSocket支持
最后,需要配置Spring Boot以支持WebSocket。创建一个新的Java类,并添加注释
@Configuration
和
@EnableWebSocket
。然后,需要覆盖方法
registerWebSocketHandlers()
,并指定WebSocket处理程序。下面是一个示例代码:
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.config.annotation.EnableWebSocket;
import org.springframework.web.socket.config.annotation.WebSocketConfigurer;
import org.springframework.web.socket.config.annotation.WebSocketHandlerRegistry;
@Configuration
@EnableWebSocket
public class WebSocketConfig implements WebSocketConfigurer {
@Override
public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
registry.addHandler(new WebSocketServer(), "/websocket").setAllowedOrigins("*");
}
}
在此代码中,我们创建了一个新的
WebSocketServer
对象,并将其添加到WebSocket处理程序中。我们还指定了WebSocket端点(
/websocket
)和允许的来源(
*
)。
四、 WebSocket的消息格式
4.1 文本消息和二进制消息
文本消息是普通的Unicode文本字符串。当WebSocket连接建立时,客户端和服务器可以通过发送文本消息来互相交换信息。服务器可以使用Session对象的
getBasicRemote()
方法来向客户端发送文本消息,客户端可以使用WebSocket的
send()
方法来向服务器发送文本消息。
下面是向客户端发送文本消息的示例代码:
session.getBasicRemote().sendText("Hello, client!");
二进制消息可以是任意类型的数据,包括图像、音频、视频等。要向客户端发送二进制消息,服务器可以使用Session对象的
getBasicRemote()
方法,将消息作为ByteBuffer对象发送。客户端可以使用WebSocket的send()方法来向服务器发送二进制消息。
下面是向客户端发送二进制消息的示例代码:
byte[] data = // binary data
ByteBuffer buffer = ByteBuffer.wrap(data);
session.getBasicRemote().sendBinary(buffer);
请注意,尽管文本消息和二进制消息在格式上有所不同,但它们都是通过WebSocket发送的消息类型,因此客户端和服务器都需要能够处理这两种类型的消息。
4.2 Ping和Pong消息
WebSocket还支持Ping和Pong消息类型,用于检测WebSocket连接是否仍然处于活动状态。Ping消息由客户端发送到服务器,Pong消息由服务器发送回客户端作为响应。如果客户端在一段时间内没有收到Pong消息,则它可以假定WebSocket连接已断开,并关闭连接。
要发送Ping消息,请使用Session对象的
getBasicRemote()
方法,并将Ping消息作为ByteBuffer对象发送。客户端可以使用WebSocket的
sendPing()
方法来向服务器发送Ping消息。
下面是向客户端发送Ping消息的示例代码:
ByteBuffer pingMessage = ByteBuffer.wrap(new byte