【计算机网络】WebSocket

WebSocket 是一种在单个 TCP 连接上实现全双工通信的协议,旨在解决传统 HTTP 协议在实时通信场景中的局限性。

1、消息推送方式

1.1 轮询(Polling)

  • 特点:
    • 客户端定期向服务器发送请求,询问是否有新消息。
    • 实现简单,但效率较低,因为即使在没有新消息的情况下,客户端也会频繁发送请求,浪费网络资源。
  • 适用场景:
    • 对实时性要求不高的应用,或者作为临时解决方案。

1.2 长轮询(Long Polling)

  • 特点:
    • 客户端向服务器发送请求后,服务器会保持连接打开,直到有新消息或超时为止。
    • 一旦有新消息,服务器立即响应并发送数据给客户端,然后客户端关闭连接并重新发起一个新的请求。
    • 相比轮询,长轮询减少了客户端的请求次数,提高了效率。
  • 适用场景:
    • 需要一定实时性,但不想使用WebSocket等更复杂技术的场景。

1.3 WebSocket

  • 特点:
    • 提供了全双工的通信通道,允许服务器和客户端在任意时刻发起消息传递。
    • 适用于实时通信和推送场景,如聊天应用、实时通知等。
    • 支持文本和二进制数据格式,具有更好的性能和可扩展性。
  • 适用场景:
    • 需要高实时性和双向通信的应用,如在线游戏、实时协作工具等。

1.4 SSE(Server-Sent Events)

  • 特点:
    • 一种允许服务器向客户端推送实时数据的技术,但仅支持从服务器到客户端的单向通信。
    • 客户端通过HTTP GET请求建立连接,服务器可以通过该连接持续向客户端发送事件流。
    • 实现简单,适用于需要实时更新但不需要客户端响应的场景。
  • 适用场景:
    • 如股票价格更新、社交媒体通知等需要实时数据更新的应用。

2、与 HTTP 的对比

  • HTTP 的局限性:
    • 单向通信:客户端发起请求,服务器响应后连接关闭。
    • 高开销:频繁的请求-响应模式导致头部重复传输。
    • 延迟高:无法实现服务器主动推送数据(需依赖轮询或长轮询)。
  • WebSocket 的优势:
    • 全双工通信:客户端和服务器可同时发送数据。
    • 低开销:连接持久化,数据帧头部仅 2~10 字节。
    • 低延迟:建立连接后,数据实时传输。

3、工作原理

  • 握手阶段(Handshake)
    • 客户端发起请求:通过 HTTP 协议发送升级请求:
      GET /chat HTTP/1.1
      Host: example.com
      Upgrade: websocket
      Connection: Upgrade
      Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
      Sec-WebSocket-Version: 13
      
    • 服务器响应:返回状态码 101 Switching Protocols 完成协议升级:
      HTTP/1.1 101 Switching Protocols
      Upgrade: websocket
      Connection: Upgrade
      Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=
      
  • 数据传输
    • 握手后,连接保持打开,双方通过数据帧(Frame)通信。
    • 支持文本(UTF-8)和二进制数据格式。
    • 通过 opcode 标识数据类型(如 0x1 表示文本,0x2 表示二进制)。

4、关键特性

  • 持久连接:无需重复握手,减少延迟。
  • 跨域支持:通过 Origin 头实现安全跨域通信。
  • 心跳机制:通过 Ping/Pong 帧检测连接状态。
  • 协议扩展:支持压缩(如 permessage-deflate)等扩展功能。

5、应用场景

  • 实时通信:聊天应用(如 Slack)、在线客服。
  • 高频数据更新:股票行情、实时游戏(如多人对战)。
  • 协同工具:在线文档编辑(如 Google Docs)、远程控制。
  • IoT 设备:传感器数据实时监控。

6、实现示例

Java原生代码实现websocket

  • 依赖:
    <dependency>
        <groupId>org.java-websocket</groupId>
        <artifactId>Java-WebSocket</artifactId>
        <version>1.5.3</version>
    </dependency>
    
  • 服务端
    package com.example.helloword;
    
    import org.java_websocket.WebSocket;
    import org.java_websocket.handshake.ClientHandshake;
    import org.java_websocket.server.WebSocketServer;
    import java.net.InetSocketAddress;
    import java.util.Collections;
    import java.util.HashSet;
    import java.util.Set;
    
    public class ChatServer extends WebSocketServer {
          
          
        private static final int PORT = 8888;
        private final Set<WebSocket> connections = Collections.synchronizedSet(new HashSet<>());
    
        public ChatServer() {
          
          
            super(new InetSocketAddress(PORT));
        }
    
        @Override
        public void onOpen(WebSocket conn, ClientHandshake handshake) {
          
          
            connections.add(conn);
            System.out.println("新客户端连接: " + conn.getRemoteSocketAddress());
            broadcast("用户[" + conn.getRemoteSocketAddress() + "] 进入聊天室");
        }
    
        @Override
        public void onClose(WebSocket conn, int code, String reason, boolean remote) {
          
          
            connections.remove(conn);
            System.out.println("客户端断开: " + conn.getRemoteSocketAddress());
            broadcast("用户[" + conn.getRemoteSocketAddress() + "] 离开聊天室");
        }
    
        @Override
        public void onMessage(WebSocket conn, String message) {
          
          
            System.out.println("收到消息: " + message);
            broadcast("用户[" + conn.getRemoteSocketAddress() + "]: " + message);
        }
    
        @Override
        public void onError(WebSocket conn, Exception ex) {
          
          
            System.err.println("发生错误: " + ex.getMessage());
        }
    
        @Override
        public void onStart() {
          
          
            System.out.println("Server started!");
        }
    
        public static void main(String[] args) {
          
          
            ChatServer server = new ChatServer();
            server.start();
            System.out.println("服务端已启动,监听端口: " + PORT);
        }
    }
    
  • 客户端
    package com.example.helloword;
    
    import org.java_websocket.client.WebSocketClient;
    import org.java_websocket.handshake.ServerHandshake;
    import java.net.URI;
    import java.util.Scanner;
    
    public class ChatClient extends WebSocketClient {
          
          
        public ChatClient(URI serverUri) {
          
          
            super(serverUri);
        }
    
        @Override
        public void onOpen(ServerHandshake handshakedata) {
          
          
            System.out.println("已连接到服务端");
        }
    
        @Override
        public void onMessage(String message) {
          
          
            System.out.println(message); // 接收服务端广播的消息
        }
    
        @Override
        public void onClose(int code, String reason, boolean remote) {
          
          
            System.out.println("连接关闭: " + reason);
        }
    
        @Override
        public void onError(Exception ex) {
          
          
            System.err.println("发生错误: " + ex.getMessage());
        }
    
        public static void main(String[] args) throws Exception {
          
          
            ChatClient client = new ChatClient(new URI("ws://localhost:8888"));
            client.connect();
    
            // 读取控制台输入并发送消息
            Scanner scanner = new Scanner(System.in);
            System.out.println("输入消息(输入 'exit' 退出):");
            String message;
            while (!(message = scanner.nextLine()).equalsIgnoreCase("exit")) {
          
          
                client.send(message);
            }
            client.close();
        }
    }
    

7、注意事项

安全性:
使用 wss://(WebSocket over TLS)加密通信。
验证 Origin 头防止跨站劫持(CSRF)。
兼容性:现代浏览器均支持,但需考虑旧版本回退方案(如长轮询)。
资源管理:长时间连接可能占用服务器资源,需合理设计连接生命周期。
总结

WebSocket 通过高效的双向通信机制,成为实时应用的理想选择。其设计在减少网络开销的同时,提供了灵活的扩展能力,适用于从即时通讯到物联网的广泛场景。开发者需结合具体需求权衡其优缺点,并注意安全与兼容性实践。

10、资料

猜你喜欢

转载自blog.csdn.net/weixin_42364929/article/details/146031465