WebSocket

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 依赖:

XML 复制代码
<!-- Maven 依赖 -->
<dependency>
    <groupId>javax.websocket</groupId>
    <artifactId>javax.websocket-api</artifactId>
    <version>1.1</version>
    <scope>provided</scope>
</dependency>

5.2 服务端代码

java 复制代码
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 客户端代码

java 复制代码
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 添加依赖

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

7.2 启用WebSocket支持

java 复制代码
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 实现消息处理器

java 复制代码
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)

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>
相关推荐
陌路物是人非1 分钟前
SpringBoot + Netty + Vue + WebSocket实现在线聊天
vue.js·spring boot·websocket·netty
南宫生11 分钟前
Java迭代器【设计模式之迭代器模式】
java·学习·设计模式·kotlin·迭代器模式
seabirdssss28 分钟前
通过动态获取项目的上下文路径来确保请求的 URL 兼容两种启动方式(IDEA 启动和 Tomcat 部署)下都能正确解析
java·okhttp·tomcat·intellij-idea
计算机毕设定制辅导-无忧学长41 分钟前
TDengine 数据写入优化:协议选择与批量操作(一)
网络·数据库·tdengine
kill bert1 小时前
第30周Java分布式入门 消息队列 RabbitMQ
java·分布式·java-rabbitmq
胡斌附体1 小时前
qt tcpsocket编程遇到的并发问题
开发语言·网络·qt·并发编程·tcpsocket
鲤籽鲲1 小时前
C# System.Net.IPAddress 使用详解
网络·c#·.net
穿林鸟2 小时前
Spring Boot项目信创国产化适配指南
java·spring boot·后端
伏游2 小时前
【BUG】生产环境死锁问题定位排查解决全过程
服务器·数据库·spring boot·后端·postgresql·bug
此木|西贝2 小时前
【设计模式】模板方法模式
java·设计模式·模板方法模式