【Websocket 】带着问题来看(一)

楔子

WebSocket 的主要优点和缺点

  1. 全双工通信

    • WebSocket 支持双向通信,客户端和服务器可以同时发送和接收数据,而不需要像 HTTP 那样依赖请求-响应模式。
  2. 低延迟

    • WebSocket 建立连接后,数据传输的延迟非常低,适合实时应用(如聊天、游戏、实时数据推送等)。
  3. 减少带宽消耗

    • WebSocket 的连接是持久的,不需要像 HTTP 那样频繁地建立和关闭连接,减少了握手和头信息的开销。
  4. 高效的数据传输

    • WebSocket 的数据帧较小,传输效率高,特别适合频繁的小数据包传输。
  5. 支持二进制和文本数据

    • WebSocket 可以传输二进制数据和文本数据,灵活性高。
  6. 跨域支持

    • WebSocket 支持跨域通信,可以通过 wss:// 实现安全的跨域数据传输。
  7. 心跳机制

    • WebSocket 支持 Ping/Pong 帧,可以用于检测连接是否存活,避免连接因超时被关闭。
  8. 易于扩展

    • WebSocket 协议支持扩展,可以通过扩展实现压缩、加密等功能。
  9. 兼容性好

    • 现代浏览器和主流编程语言都支持 WebSocket,易于集成到现有系统中。
  10. 减少服务器压力

  • 相比 HTTP 轮询,WebSocket 的持久连接减少了服务器的并发压力。

WebSocket 是基于什么协议实现的

WebSocket 是基于 TCP 协议 实现的。具体来说,WebSocket 是一个应用层协议,它依赖于传输层的 TCP 协议来提供可靠的双向通信。


WebSocket 和 TCP 的关系

  1. 传输层协议:TCP

    • WebSocket 使用 TCP 作为底层传输协议,确保数据的可靠传输(无丢失、无重复、按序到达)。
    • TCP 提供了面向连接、可靠的字节流服务,适合 WebSocket 的实时通信需求。
  2. 应用层协议:WebSocket

    • WebSocket 是一个独立的应用层协议,定义了自己的握手过程、数据帧格式和通信规则。
    • 它在 TCP 之上运行,利用 TCP 提供的可靠传输能力,实现了全双工通信。

WebSocket 的数据帧

WebSocket 定义了自己的数据帧格式,用于传输文本或二进制数据。数据帧包括以下部分:

  • FIN :指示是否是消息的最后一帧。
  • Opcode:指示帧的类型(如文本帧、二进制帧、关闭帧等)。
  • Mask :指示是否使用掩码(客户端到服务器的帧必须掩码)。
  • Payload length:指示数据的长度。
  • Payload data:实际传输的数据。

WebSocket 连接的建立分为两个阶段:

  1. HTTP 握手阶段:客户端和服务器通过 HTTP 协议进行握手,协商升级到 WebSocket 协议。
  2. WebSocket 通信阶段:握手成功后,客户端和服务器使用 WebSocket 协议进行双向通信。

WebSocket 连接建立的详细过程

step 1 :客户端发起握手请求

客户端发送一个 HTTP GET 请求,请求头中包含以下关键字段:

  • Upgrade: websocket:表示客户端希望升级到 WebSocket 协议。
  • Connection: Upgrade:表示客户端希望升级连接。
  • Sec-WebSocket-Key:一个随机生成的 Base64 编码字符串,用于握手验证。
  • Sec-WebSocket-Version:指定 WebSocket 协议版本(通常是 13)。

示例:

http 复制代码
GET /chat HTTP/1.1
Host: example.com
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
Sec-WebSocket-Version: 13
step 2 服务器响应握手

服务器收到客户端的握手请求后,如果同意升级到 WebSocket 协议,会返回一个 HTTP 101 状态码(Switching Protocols),并在响应头中包含以下关键字段:

  • Upgrade: websocket:表示服务器同意升级到 WebSocket 协议。
  • Connection: Upgrade:表示服务器同意升级连接。
  • Sec-WebSocket-Accept :服务器根据客户端的 Sec-WebSocket-Key 计算出的验证字符串。

Sec-WebSocket-Accept 的计算方法:

  1. 将客户端的 Sec-WebSocket-Key 与固定的 GUID(258EAFA5-E914-47DA-95CA-C5AB0DC85B11)拼接。
  2. 对拼接后的字符串进行 SHA-1 哈希计算。
  3. 将哈希结果进行 Base64 编码。

示例:

http 复制代码
HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=
step3 升级到 WebSocket 协议
diff 复制代码
-   握手完成后,客户端和服务器之间的通信将使用 WebSocket 协议,不再使用 HTTP。

WebSocket 的数据帧

WebSocket 定义了自己的数据帧格式,用于传输文本或二进制数据。数据帧包括以下部分:

  1. FIN:1 位,表示是否是消息的最后一帧。
  2. RSV1, RSV2, RSV3:各 1 位,保留字段,通常为 0。
  3. Opcode:4 位,表示帧的类型(如文本帧、二进制帧、关闭帧等)。
  4. Mask:1 位,指示是否使用掩码。
  5. Payload length:7 位、7+16 位或 7+64 位,表示数据的长度。
  6. Masking key:4 字节(如果 Mask 为 1),用于掩码计算。
  7. Payload data:实际的数据。
C 复制代码
// WebSocket 数据帧结构体
typedef struct {
  uint8_t fin : 1;       // FIN 标志位
  uint8_t rsv1 : 1;      // RSV1 标志位
  uint8_t rsv2 : 1;      // RSV2 标志位
  uint8_t rsv3 : 1;      // RSV3 标志位
  uint8_t opcode : 4;    // 操作码(Opcode)
  uint8_t mask : 1;      // 掩码标志位
  uint8_t payload_len : 7; // 数据长度(7 位或扩展)
  uint32_t masking_key;  // 掩码键(4 字节)
  uint8_t* payload_data; // 数据内容
} WebSocketFrame;

WebSocket 是全双工还是半双工

WebSocket 是 全双工(Full-Duplex) 的通信协议。

全双工 vs 半双工

  1. 全双工(Full-Duplex)

    • 通信双方可以同时发送和接收数据。
    • 例如:电话通话,双方可以同时说话和听对方说话。
  2. 半双工(Half-Duplex)

    • 通信双方可以发送和接收数据,但不能同时进行。
    • 例如:对讲机,同一时间只能有一方说话,另一方听。

WebSocket 的握手请求头中有哪些关键字段

WebSocket 握手请求头中的关键字段包括:

  • Upgrade
  • Connection
  • Sec-WebSocket-Key
  • Sec-WebSocket-Version
  • Host
  • Origin(可选)
  • Sec-WebSocket-Protocol(可选)
  • Sec-WebSocket-Extensions(可选)

解释

1. Upgrade

  • 作用:表示客户端希望将连接升级到 WebSocket 协议。

  • :必须为 websocket

  • 示例

    http 复制代码
    Upgrade: websocket

2. Connection

  • 作用:表示客户端希望升级连接。

  • :必须为 Upgrade

  • 示例

    http 复制代码
    Connection: Upgrade

3. Sec-WebSocket-Key

  • 作用:客户端随机生成的一个 Base64 编码的字符串,用于握手验证。

  • :16 字节随机数的 Base64 编码。

  • 示例:

    http 复制代码
    Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==

4. Sec-WebSocket-Version

  • 作用:指定客户端支持的 WebSocket 协议版本。

  • :当前标准版本为 13

  • 示例

    http 复制代码
    Sec-WebSocket-Version: 13

5. Host

  • 作用:指定服务器的域名或 IP 地址。

  • :服务器的地址。

  • 示例

    http 复制代码
    Host: example.com

6. Origin(可选)

  • 作用:表示请求的来源,用于跨域验证。

  • :客户端的源地址(协议 + 域名 + 端口)。

  • 示例

    http 复制代码
    Origin: http://example.com

7. Sec-WebSocket-Protocol(可选)

  • 作用 :指定客户端支持的子协议(如 chatsuperchat 等)。

  • :子协议名称。

  • 示例

    http 复制代码
    Sec-WebSocket-Protocol: chat, superchat

8. Sec-WebSocket-Extensions(可选)

  • 作用:指定客户端支持的扩展(如压缩、加密等)。

  • :扩展名称。

  • 示例

    http 复制代码
    Sec-WebSocket-Extensions: permessage-deflate

完整的 WebSocket 握手请求示例

http 复制代码
GET /chat HTTP/1.1
Host: example.com
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
Sec-WebSocket-Version: 13
Origin: http://example.com
Sec-WebSocket-Protocol: chat, superchat
Sec-WebSocket-Extensions: permessage-deflate

WebSocket 的最大帧大小是多少

  1. 协议设计

    • WebSocket 数据帧的 Payload length 字段可以表示的最大长度为 2^63 - 1 字节(约 9.22 EB,Exabytes)。
    • 这是一个理论值,实际应用中不会达到这个大小。
  2. 实现限制

    • 客户端和服务器的实现可能会对帧大小设置限制。
    • 例如,某些 WebSocket 库可能默认限制帧大小为 16 MB 或更小。
  3. 网络和硬件限制

    • 网络带宽、内存和硬件资源也会影响帧大小的实际限制。
    • Java 的 Java-WebSocket
java 复制代码
import org.java_websocket.server.WebSocketServer;

WebSocketServer server = new WebSocketServer(new InetSocketAddress(8765)) {
    @Override
    public void onOpen(WebSocket conn, ClientHandshake handshake) {}

    @Override
    public void onMessage(WebSocket conn, String message) {}

    @Override
    public void onError(WebSocket conn, Exception ex) {}

    @Override
    public void onStart() {}
};

server.setMaxFrameSize(100 * 1024 * 1024); // 设置最大帧大小为 100 MB
server.start();
  • WebSocket 协议本身没有明确规定最大帧大小,理论最大长度为 2^63 - 1 字节
  • 实际应用中,浏览器和服务器通常会对帧大小设置限制(如 16 MB)。
  • 如果需要传输大文件或大数据,可以将数据分片传输,或通过配置调整帧大小限制

WebSocket 的连接状态有哪些

WebSocket 的连接状态包括:

  1. CONNECTING:连接中。
  2. OPEN:连接已打开,可以通信。
  3. CLOSING:连接关闭中。
  4. CLOSED:连接已关闭。

通过监听 onopenoncloseonerror 事件,可以实时获取连接状态的变化。

WebSocket 的连接是如何关闭的?

1. 发送关闭帧(Close Frame)
  • 关闭帧的格式

    • Opcode0x8(表示关闭帧)。
    • Payload:可选的关闭原因(状态码和原因短语)。
  • 状态码

    • 2 字节的无符号整数,表示关闭原因。
    • 例如:1000(正常关闭)、1001(端点离开)等。
  • 原因短语

    • 可选的 UTF-8 编码字符串,描述关闭原因。
2. 接收关闭帧
  • 当一端收到关闭帧后,必须发送一个关闭帧作为响应。
  • 收到关闭帧后,连接进入 CLOSING 状态。
3. 关闭 TCP 连接
  • 在双方都发送和接收了关闭帧后,连接进入 CLOSED 状态。
  • 底层 TCP 连接被关闭。

WebSocket 关闭连接的示例

1. 客户端主动关闭连接

javascript

Copy

javascript 复制代码
const socket = new WebSocket('ws://example.com');

// 监听连接打开事件
socket.onopen = () => {
    console.log('WebSocket 连接已打开');
    // 主动关闭连接
    socket.close(1000, 'Normal Closure');
};

// 监听连接关闭事件
socket.onclose = (event) => {
    console.log('WebSocket 连接已关闭');
    console.log('状态码:', event.code);
    console.log('原因:', event.reason);
};
2. 服务器主动关闭连接(Node.js + ws 库)
js 复制代码
const WebSocket = require('ws');
const wss = new WebSocket.Server({ port: 8080 });

wss.on('connection', (ws) => {
    console.log('客户端已连接');

    // 服务器主动关闭连接
    ws.close(1000, 'Normal Closure');

    ws.on('close', (code, reason) => {
        console.log('客户端已断开连接');
        console.log('状态码:', code);
        console.log('原因:', reason.toString());
    });
});

WebSocket 关闭连接的生命周期

  1. OPEN → CLOSING

    • 当一端发送关闭帧后,连接进入 CLOSING 状态。
  2. CLOSING → CLOSED

    • 当另一端也发送关闭帧后,连接进入 CLOSED 状态。
  3. 异常关闭

    • 如果未收到关闭帧(如网络中断),连接直接进入 CLOSED 状态。

WebSocket 的关闭帧是什么 (opcode = 0x8)

WebSocket 连接的关闭是通过 关闭握手 实现的:

  1. 发送关闭帧(包含状态码和原因短语)。
  2. 接收关闭帧并响应。
  3. 关闭底层 TCP 连接。

通过监听 onclose 事件,可以获取关闭的状态码和原因,便于调试和处理异常情况。

WebSocket 的关闭帧是什么

WebSocket 的 关闭帧(Close Frame) 是 WebSocket 协议中用于关闭连接的一种特殊数据帧。 关闭帧的作用是通知对方连接即将关闭,并可以携带关闭原因(状态码和原因短语)。


关闭帧的结构

关闭帧是 WebSocket 数据帧的一种,其结构如下:

字段 长度 描述
FIN 1 位 表示是否是消息的最后一帧(关闭帧必须为 1)。
RSV1, RSV2, RSV3 各 1 位 保留字段,必须为 0
Opcode 4 位 操作码,关闭帧的值为 0x8
Mask 1 位 指示是否使用掩码(客户端到服务器的帧必须掩码)。
Payload length 7/7+16/7+64 位 数据长度(关闭帧的 Payload 长度可以为 0 或 2 字节以上)。
Masking key 4 字节(如果 Mask 为 1) 掩码键(客户端到服务器的帧必须掩码)。
Payload data 可变长度 关闭帧的有效载荷,包含状态码和原因短语(可选)。

关闭帧的有效载荷(Payload Data)

关闭帧的有效载荷可以包含以下内容:

  1. 状态码(Status Code)

    • 2 字节的无符号整数,表示关闭原因。
    • 例如:1000(正常关闭)、1001(端点离开)等。
  2. 原因短语(Reason Phrase)

    • 可选的 UTF-8 编码字符串,描述关闭原因。
    • 最大长度为 123 字节(因为 WebSocket 帧的最大 Payload 长度为 125 字节,状态码占 2 字节)。

关闭帧的示例

以下是一个关闭帧的示例:

  • 状态码1000(正常关闭)。
  • 原因短语"Normal Closure"
关闭帧的字节流
字节位置 值(十六进制) 描述
0 0x88 FIN=1, RSV1=0, RSV2=0, RSV3=0, Opcode=0x8(关闭帧)。
1 0x82 Mask=1, Payload length=2(状态码占 2 字节)。
2-5 0x37 0xFA 0x21 0x3D 掩码键(随机生成的 4 字节)。
6-7 0x03 0xE8 状态码 10000x03E81000 的十六进制表示)。
8-15 0x34 0x9F 0x45 0x6E 0x6F 0x72 0x6D 0x61 原因短语 "Normal Closure" 的掩码数据。

关闭帧的发送与接收

1. 发送关闭帧
  • 客户端或服务器可以调用 close() 方法发送关闭帧。

  • 示例(JavaScript):

    javascript 复制代码
    const socket = new WebSocket('ws://example.com');
    socket.close(1000, 'Normal Closure');
2. 接收关闭帧
  • 当一端收到关闭帧后,必须发送一个关闭帧作为响应。

  • 示例(JavaScript):

    javascript 复制代码
    socket.onclose = (event) => {
        console.log('状态码:', event.code);
        console.log('原因:', event.reason);
    };

关闭帧的状态码

WebSocket 协议定义了一些标准的状态码,用于表示关闭原因:

状态码 名称 描述
1000 Normal Closure 正常关闭
1001 Going Away 端点离开(如服务器关闭或浏览器导航离开)
1002 Protocol Error 协议错误
1003 Unsupported Data 接收到不支持的数据(如文本帧接收二进制数据)
1004 Reserved 保留
1005 No Status Rcvd 未收到状态码(用于占位)
1006 Abnormal Closure 异常关闭(如未发送关闭帧)
1007 Invalid Frame Payload Data 接收到无效的数据(如非 UTF-8 文本数据)
1008 Policy Violation 策略违规
1009 Message Too Big 消息过大
1010 Mandatory Extension 需要扩展
1011 Internal Server Error 服务器内部错误
1015 TLS Handshake TLS 握手失败

总结

WebSocket 的关闭帧是用于关闭连接的特殊数据帧,其结构包括:

  • Opcode0x8
  • Payload:可选的关闭原因(状态码和原因短语)。

通过发送和接收关闭帧,WebSocket 连接可以优雅地关闭,并传递关闭原因

WebSocket 的心跳机制是什么

WebSocket 的 心跳机制(Heartbeat Mechanism) 是一种用于检测连接是否存活的机制。通过定期发送和接收 Ping/Pong 帧,客户端和服务器可以确认对方是否仍然在线,从而避免因长时间无通信而导致的连接超时或断开。

心跳机制的作用

  1. 检测连接状态

    • 通过定期发送 Ping 帧,检测对方是否仍然在线。
  2. 防止连接超时

    • 如果连接长时间无通信,可能会被防火墙、代理服务器或操作系统关闭。心跳机制可以保持连接活跃。
  3. 快速发现断线

    • 如果未收到 Pong 帧响应,可以快速发现连接已断开。

Ping/Pong 帧

WebSocket 协议定义了两种控制帧用于心跳机制:

  1. Ping 帧

    • Opcode:0x9
    • 用于发送心跳检测。
    • 可以携带少量数据(如时间戳)。
  2. Pong 帧

    • Opcode:0xA
    • 用于响应 Ping 帧。
    • 必须携带与对应 Ping 帧相同的数据。

心跳机制的实现

1. 客户端发送 Ping 帧

客户端可以定期向服务器发送 Ping 帧,检测服务器是否在线。

2. 服务器响应 Pong 帧

服务器收到 Ping 帧后,必须发送一个 Pong 帧作为响应。

3. 超时处理

如果客户端在指定时间内未收到 Pong 帧,可以认为连接已断开,并进行重连或其他处理。


心跳机制的示例

以下是一个简单的心跳机制实现示例:

1. 客户端实现(JavaScript)
javascript 复制代码
const socket = new WebSocket('ws://example.com');

// 定时发送 Ping 帧
const heartbeatInterval = 30000; // 30 秒
let heartbeatTimer;

socket.onopen = () => {
    console.log('WebSocket 连接已打开');
    // 启动心跳机制
    heartbeatTimer = setInterval(() => {
        if (socket.readyState === WebSocket.OPEN) {
            socket.send('Ping'); // 发送 Ping 帧
        }
    }, heartbeatInterval);
};

// 监听 Pong 帧
socket.onmessage = (event) => {
    if (event.data === 'Pong') {
        console.log('收到 Pong 帧');
    }
};

// 监听连接关闭事件
socket.onclose = () => {
    console.log('WebSocket 连接已关闭');
    // 清除心跳定时器
    clearInterval(heartbeatTimer);
};
2. 服务器实现(Node.js + ws 库)
javascript 复制代码
const WebSocket = require('ws');
const wss = new WebSocket.Server({ port: 8080 });

wss.on('connection', (ws) => {
    console.log('客户端已连接');

    // 监听 Ping 帧
    ws.on('ping', (data) => {
        console.log('收到 Ping 帧');
        // 发送 Pong 帧
        ws.pong(data);
    });

    // 监听连接关闭事件
    ws.on('close', () => {
        console.log('客户端已断开连接');
    });
});

心跳机制的优化

  1. 动态调整心跳间隔

    • 根据网络状况动态调整心跳间隔,避免频繁发送 Ping 帧。
  2. 超时重连

    • 如果未收到 Pong 帧,可以尝试重连。
  3. 携带数据

    • 在 Ping 帧中携带时间戳或其他数据,用于计算延迟或诊断问题。

总结

WebSocket 的心跳机制通过 Ping/Pong 帧 实现,用于检测连接是否存活。客户端定期发送 Ping 帧,服务器响应 Pong 帧。如果未收到 Pong 帧,可以认为连接已断开,并进行相应处理。心跳机制是保持 WebSocket 连接稳定的重要手段。

如何实现 WebSocket 的加密传输(wss)

实现 WebSocket 的加密传输(wss)的步骤如下:

  1. 获取 SSL/TLS 证书。
  2. 配置服务器支持 wss
  3. 客户端通过 wss:// 协议连接服务器。

通过 wss,可以确保 WebSocket 通信的安全性,防止数据被窃听或篡改。

如何处理 WebSocket 跨域问题

览器跨域问题是指在Web开发中,由于浏览器的同源策略(Same-Origin Policy)限制,导致在一个域下的网页无法与其他域(协议、域名、端口有任何一个不同即为不同域)的资源进行交互或通信的情况
使用websocket解决跨域,首先也是需要服务端支持websocket相关的协议,可以用于两个页面之间的通信,A页面和A服务器正常通信,B页面通过websocket和A服务器通信,从而达到A页面和B页面的通信。这其实也是一个比较彻底的方案,绕过了浏览器的限制,如果有两个页面的通信,可以使用这个方案。不过我的经验,在日常中,websocket主要还是做服务端向前端推送的工作,做跨域的比较少。

  • 当浏览器从一个域名下的页面向另一个域名服务器发起WebSocket连接时,就会涉及到跨域问题,
  • WebSocket 是一种在客户端和服务器之间建立持久连接的通信协议,它可以用于实时通信和数据传输。由于 WebSocket 是一种独立的协议,不受同源策略的限制,因此它可以轻松解决跨域问题。下面是 WebSocket 如何解决跨域问题的简要说明:
  1. 不受同源策略限制:WebSocket 协议不受同源策略的限制,允许在不同域之间建立连接。
  2. 独立的协议:WebSocket 使用自己的协议,与 HTTP/HTTPS 不同,因此不受同源策略的限制。在初始握手期间,服务器可以使用适当的响应头来允许跨域连接。
  3. 服务器设置 :要允许跨域 WebSocket 连接,服务器端需要设置适当的响应头。通常,服务器会设置 Access-Control-Allow-Origin 头来指定允许的来源域。例如,可以将其设置为 * 表示允许来自任何域的连接。

服务器端如何设置允许跨域 WebSocket 连接:

javascript 复制代码
const WebSocket = require('ws');
const wss = new WebSocket.Server({ port: 8080 });

wss.on('connection', function connection(ws) {
  ws.on('message', function incoming(message) {
    console.log('received: %s', message);
  });

  ws.send('something');
});

// 设置允许跨域连接
wss.on('headers', (headers, req) => {
  headers['Access-Control-Allow-Origin'] = '*';
});

通过上述设置,WebSocket 可以轻松解决跨域问题,实现不同域之间的实时通信和数据传输。

如何在 WebSocket 中实现身份认证

  • 建立连接时验证身份 :在客户端发起 WebSocket 连接时,可以在握手阶段进行身份验证。这可以通过在连接 URL 中传递身份信息(如 token)或在连接的 HTTP 头部中包含身份验证信息来实现。

  • 验证身份信息:在服务端接收到 WebSocket 连接请求后,可以解析连接 URL 或读取 HTTP 头部中的身份验证信息,并进行验证。这可能涉及检查 token 的有效性、检查用户权限等操作。

  • 认证成功后建立连接:如果身份验证成功,服务端可以接受连接并与客户端建立 WebSocket 连接。否则,可以拒绝连接或断开连接。

  • 维护身份信息:一旦身份验证成功,服务端可以在连接建立后维护用户的身份信息,以便在后续通信中使用。

c 复制代码
#include <libwebsockets.h>
#include <string.h>

static int callback_http(struct lws *wsi, enum lws_callback_reasons reason, void *user, void *in, size_t len) {
    return 0;
}

static int callback_websocket(struct lws *wsi, enum lws_callback_reasons reason, void *user, void *in, size_t len) {
    switch (reason) {
        case LWS_CALLBACK_ESTABLISHED: {
            // 在连接建立时进行身份验证
            const char *token = lws_hdr_simple_ptr(wsi, WSI_TOKEN_GET_URI);
            if (strcmp(token, "valid_token") == 0) {
                // 身份验证成功,向客户端发送消息
                char msg[] = "Authentication successful. You are now connected.";
                lws_write(wsi, (unsigned char *)msg, strlen(msg), LWS_WRITE_TEXT);
            } else {
                // 身份验证失败,关闭连接
                char msg[] = "Authentication failed. Connection refused.";
                lws_write(wsi, (unsigned char *)msg, strlen(msg), LWS_WRITE_TEXT);
                lws_close_reason(wsi, LWS_CLOSE_STATUS_POLICY_VIOLATION, NULL, 0);
            }
            break;
        }
        default:
            break;
    }

    return 0;
}

static struct lws_protocols protocols[] = {
    {
        "http-only",
        callback_http,
        0,
    },
    {
        "websocket",
        callback_websocket,
        0,
    },
    { NULL, NULL, 0, 0 }
};

int main() {
    struct lws_context_creation_info info;
    memset(&info, 0, sizeof(info));

    struct lws_context *context = lws_create_context(&info);
    if (!context) {
        return -1;
    }

    int port = 8080;
    const char *iface = NULL;

    struct lws_vhost *vhost = lws_create_vhost(context, &port, iface, NULL, NULL, NULL, NULL);
    if (!vhost) {
        lws_context_destroy(context);
        return -1;
    }

    while (1) {
        lws_service(context, 50);
    }

    lws_context_destroy(context);

    return 0;
}
相关推荐
Wo3Shi4七3 分钟前
MySQL JOIN 和 GROUP BY
数据库·后端
一只小闪闪9 分钟前
langchain4j搭建失物招领系统(一)---初始化项目
java·人工智能·后端
独立开阀者_FwtCoder13 分钟前
penAI重磅发布GPT-4o文生图:免费、精准、媲美真实照片!
前端·后端·面试
Fade_VV22 分钟前
记录一个复合嵌套类的使用MyBatis取出数据的例子
后端
倚栏听风雨35 分钟前
Class.getComponentType()详解
后端
三原1 小时前
项目管理:这个bug是你的问题!这个不是bug是需求变更!
前端·后端·团队管理
华洛1 小时前
实战派!百万PV的AI产品如何搭建RAG系统?
前端·javascript·后端
javaTodo1 小时前
Mysql索引实现原理和相关数据结构算法
后端
tomla1 小时前
震惊!遍历集合时直接调用remove()可能会出现问题!
后端
罗念笙1 小时前
Java 的 BigDecimal 是什么?
java·后端