WebSocket

发布于:2025-04-05 ⋅ 阅读:(16) ⋅ 点赞:(0)

WebSocket

        WebSocket是一种基于TCP的网络通信协议,专为实时双向通信设计。它允许客户端和服务器在单个持久连接上双向传输数据,克服了传统HTTP协议单向请求-响应模式的局限性,适用于需要低延迟、高实时性的场景(如聊天、游戏、股票行情、实时数据推送等)。

1. WebSocket是什么

  • 全双工通信协议:允许客户端(如浏览器)和服务器同时双向传输数据,类似电话通话,双方可随时发送信息。
  • 持久连接:一次握手建立连接后,连接保持开放,数据传输无需重复建立连接,大幅降低延迟。
  • 基于TCP:运行在传输层TCP协议之上,端口与HTTP一致(80/443),但使用独立协议 ws:// 或加密的 wss://

2. WebSocket与HTTP对比

特性 HTTP WebSocket
通信模式 半双工(请求-响应) 全双工(双向实时通信)
连接生命周期 短连接(每次请求后关闭) 长连接(持久连接,可复用)
数据开销 每次请求需携带完整Header 首次握手后,数据帧轻量(2~10字节)
实时性 依赖轮询(如AJAX轮询) 支持服务端主动推送
适用场景 静态资源获取、表单提交 实时聊天、在线协作、金融行情等

3. 为什么需要WebSocket

传统HTTP的问题:

  • 单向通信:只能客户端发起请求,服务器被动响应。
  • 高开销:每次请求需携带头部信息(如Cookie、User-Agent),浪费带宽。
  • 实时性差:依赖轮询(定时请求)或长轮询(挂起等待响应),导致延迟高、资源浪费。

WebSocket的优势:

  • 低延迟:消息即时到达,适合实时应用。
  • 高效传输:连接建立后,数据帧头部极小(仅2~10字节)。
  • 节省资源:避免频繁建立连接和冗余头部。

4. WebSocket工作原理

4.1. 握手阶段(HTTP升级)

        WebSocket连接通过HTTP协议发起,客户端发送一个包含Upgrade头的请求,服务器返回101 Switching Protocols 确认升级。

客户端请求示例:

GET /chat HTTP/1.1
Host: example.com
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
Sec-WebSocket-Version: 13

服务器响应示例:

HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=

4.2 数据传输阶段

        握手成功后,双方通过数据帧(Frame)通信,格式轻量且支持分片传输。数据帧包含以下字段:

  • Opcode:标识数据类型(如文本 0x1、二进制 0x2)。
  • Payload:实际传输的数据。
  • FIN:标记是否为消息的最后一帧。

5. 使用 Java WebSocket API  编写 WebSocket 服务端

5.1 依赖配置

添加 javax.websocket-api 依赖:

<!-- Maven 依赖 -->
<dependency>
    <groupId>javax.websocket</groupId>
    <artifactId>javax.websocket-api</artifactId>
    <version>1.1</version>
    <scope>provided</scope>
</dependency>

5.2 服务端代码

import javax.websocket.*;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;

@ServerEndpoint("/chat")  // 定义 WebSocket 端点路径
public class ChatServer {
    private static final Set<Session> sessions = Collections.synchronizedSet(new HashSet<>());

    @OnOpen
    public void onOpen(Session session) {
        sessions.add(session);
        System.out.println("客户端连接: " + session.getId());
    }

    @OnMessage
    public void onMessage(String message, Session session) {
        System.out.println("收到消息: " + message);
        broadcast("用户 " + session.getId() + ": " + message);
    }

    @OnClose
    public void onClose(Session session) {
        sessions.remove(session);
        System.out.println("客户端断开: " + session.getId());
    }

    @OnError
    public void onError(Session session, Throwable error) {
        error.printStackTrace();
    }

    private void broadcast(String message) {
        sessions.forEach(session -> {
            try {
                session.getBasicRemote().sendText(message);
            } catch (IOException e) {
                e.printStackTrace();
            }
        });
    }
}

5.3 部署

将应用部署到支持Java WebSocket的服务器(如Tomcat 8+、Jetty 9+)。

6. 使用 Java WebSocket API 编写 WebSocket 客户端

6.1 客户端代码

import javax.websocket.*;
import java.net.URI;

@ClientEndpoint
public class ChatClient {
    private Session session;

    public ChatClient(String endpoint) throws Exception {
        WebSocketContainer container = ContainerProvider.getWebSocketContainer();
        container.connectToServer(this, new URI(endpoint));
    }

    @OnOpen
    public void onOpen(Session session) {
        this.session = session;
        System.out.println("已连接到服务器");
    }

    @OnMessage
    public void onMessage(String message) {
        System.out.println("收到服务器消息: " + message);
    }

    @OnClose
    public void onClose() {
        System.out.println("连接已关闭");
    }

    public void sendMessage(String message) throws IOException {
        session.getBasicRemote().sendText(message);
    }

    public static void main(String[] args) throws Exception {
        ChatClient client = new ChatClient("ws://localhost:8080/your-app/chat");
        client.sendMessage("Hello Server!");
    }
}

7. 使用 Spring Boot 编写 WebSocket 服务端

7.1 添加依赖

<!-- Spring Boot WebSocket 支持 -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-websocket</artifactId>
</dependency>

7.2 启用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(myHandler(), "/chat")  // 注册处理器和路径
                .setAllowedOrigins("*");  // 允许跨域
    }

    @Bean
    public WebSocketHandler myHandler() {
        return new ChatWebSocketHandler();
    }
}

7.3 实现消息处理器

import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketSession;
import org.springframework.web.socket.handler.TextWebSocketHandler;
import java.util.concurrent.CopyOnWriteArrayList;

public class ChatWebSocketHandler extends TextWebSocketHandler {
    private final CopyOnWriteArrayList<WebSocketSession> sessions = new CopyOnWriteArrayList<>();

    @Override
    public void afterConnectionEstablished(WebSocketSession session) {
        sessions.add(session);
        System.out.println("新连接: " + session.getId());
    }

    @Override
    protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {
        String payload = message.getPayload();
        System.out.println("收到消息: " + payload);
        broadcast("用户 " + session.getId() + ": " + payload);
    }

    @Override
    public void afterConnectionClosed(WebSocketSession session, CloseStatus status) {
        sessions.remove(session);
        System.out.println("连接关闭: " + session.getId());
    }

    private void broadcast(String message) {
        sessions.forEach(s -> {
            try {
                s.sendMessage(new TextMessage(message));
            } catch (IOException e) {
                e.printStackTrace();
            }
        });
    }
}

7.4 客户端连接(javascript)

<script>
const socket = new WebSocket('ws://localhost:8080/chat');

socket.onopen = () => {
    socket.send('Hello Spring WebSocket!');
};

socket.onmessage = (event) => {
    console.log('收到消息:', event.data);
};
</script>