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>
相关推荐
贞明电子5 小时前
工业三色报警灯怎么接入项目系统?开关量与RS485控制应用指南
运维·网络·微信公众平台·贞明电子
明天也要努力学习5 小时前
HTTP 协议的基本格式和 fiddler 的用法
网络协议·http·fiddler
学长毕设5 小时前
基于SpringBoot的实验室资产管理系统
java·spring boot·后端
HideF5 小时前
Linux
linux·服务器·网络
万联WANFLOW5 小时前
海外网络故障怎么排查?Cloudflare 三季度断网报告解读与 mtr 实战
运维·网络·故障分析
sbjdhjd5 小时前
网工进阶笔记:H3CSE 园区网全栈技术与协议汇总
网络·笔记·云计算·ssh·网络工程·vlan·aaa
zzu123zsw6 小时前
从臃肿的模板调用到声明式契约:深入 UniHttp 动态代理与多渠道生命周期扩展
java·spring boot·microsoft·unihttp
源码集结号7 小时前
云诊所门诊系统源码,基于SpringBoot+Vue.js架构,支持多租户设计与Docker容器化部署
java·spring boot·源码·医院信息系统·门诊系统·诊所系统
论文复现现场8 小时前
WRF、FEA 能用 L40S 加速吗?求解器检查、断点恢复与长期运行
java·数据库·算法·wrf·有限元分析·gpu计算·断点续算
taktok_ai8 小时前
用 cc-switch 连接 Taktok
网络