# WebSocket 适配

本文档介绍如何在 Emscripten 项目中集成微信小游戏 WebSocket 通信模块。

# 概述

本模块提供完整的 WebSocket 客户端实现,支持 WS 和 WSS 协议,适用于实时通信场景。

# 功能特性

功能 说明
协议支持 WS (ws://) 和 WSS (wss://)
消息类型 文本消息和二进制数据
自定义头 支持设置 HTTP 请求头
子协议 支持 Sec-WebSocket-Protocol
压缩 支持 permessage-deflate
并发连接 最多 5 个并发连接

# ⚠️ 重要提示

微信小游戏环境限制:

  • 真机必须使用 WSS 协议(wss://)- 加密连接
  • 不支持 WS 协议(ws://)- 非加密连接仅微信开发者工具支持
  • 需要在微信公众平台配置域名白名单(生产环境)

# 快速开始

# 集成到项目

# CMakeLists.txt 配置

cmake_minimum_required(VERSION 3.10)
project(MyGame)

set(WXGAMESDK_DIR "${CMAKE_CURRENT_SOURCE_DIR}/wxgamesdk")

add_executable(mygame main.cpp)

target_include_directories(mygame PRIVATE
    ${WXGAMESDK_DIR}/include
)

target_link_libraries(mygame
    ${WXGAMESDK_DIR}/lib/libwxgamesdk.a
)

set_target_properties(mygame PROPERTIES
    LINK_FLAGS "\
        -s WASM=1 \
        -s EXPORTED_RUNTIME_METHODS='[\"ccall\", \"cwrap\", \"stringToUTF8\", \"UTF8ToString\", \"lengthBytesUTF8\", \"allocateUTF8\"]' \
        -s EXPORTED_FUNCTIONS='[\
            \"_WebSocketOnSuccess\", \
            \"_WebSocketOnError\", \
            \"_WebSocketOnClose\", \
            \"_WebSocketOnStrData\", \
            \"_WebSocketOnBinData\", \
            \"_main\", \"_free\", \"_malloc\"]' \
        --js-library ${WXGAMESDK_DIR}/jslib/libwxwebsocket_open.jslib \
        -Wl,--whole-archive ${WXGAMESDK_DIR}/lib/libwxgamesdk.a -Wl,--no-whole-archive"
)

# API 参考

# 头文件

#include "websocket/wx_websocket_interface.h"

# 客户端接口

namespace WXSocket {
    
    // WebSocket 客户端接口类
    class WebSocketClientInterface {
    public:
        // 配置连接
        virtual int SetConnectUrl(const std::string& url) = 0;
        virtual int SetHeader(const std::string& key, const std::string& value) = 0;
        virtual int SetTimeout(int timeout) = 0;
        
        // 设置回调
        virtual int SetMessageReceiveCallback(
            void (*callback)(const void* message, int len, void* user_data),
            void* user_data) = 0;
        virtual int SetSuccessCallback(
            void (*callback)(const std::unordered_map<std::string, std::string>& header, void* user_data),
            void* user_data) = 0;
        virtual int SetFailCallback(
            void (*callback)(const std::string& errmsg, void* user_data),
            void* user_data) = 0;
        virtual int SetCloseCallback(
            void (*callback)(int code, const std::string& reason, void* user_data),
            void* user_data) = 0;
        
        // 高级选项
        virtual int SetProtocols(const std::vector<std::string>& protocols) = 0;
        virtual int SetEnableTcpNoDelay(bool enable) = 0;
        virtual int SetPerMessageDeflate(bool enable) = 0;
        virtual int SetEnableForceCellularNetwork(bool enable) = 0;
    };
    
    // 全局函数
    WebSocketClientInterface* CreateWebSocketClient(int* client_id);
    int ConnectWebSocket(int client_id);
    int SendWebSocketMessage(int client_id, const void* message, int len);
    int CloseWebSocketClient(int client_id, int code = 1000, const std::string& reason = "");
}

# 使用示例

# 基本使用

#include <emscripten.h>
#include "websocket/wx_websocket_interface.h"
#include <iostream>

using namespace WXSocket;

// 全局变量
static int g_client_id = -1;
static WebSocketClientInterface* g_ws_client = nullptr;

// 消息接收回调
void OnMessageReceived(const void* message, int len, void* user_data) {
    std::string msg((char*)message, len);
    std::cout << "[WebSocket] 收到消息: " << msg << std::endl;
}

// 连接成功回调
void OnConnectSuccess(const std::unordered_map<std::string, std::string>& header, 
                      void* user_data) {
    std::cout << "[WebSocket] 连接成功!" << std::endl;
    
    // 发送消息
    const char* msg = "Hello WebSocket!";
    SendWebSocketMessage(g_client_id, msg, strlen(msg));
}

// 连接失败回调
void OnConnectFail(const std::string& errmsg, void* user_data) {
    std::cout << "[WebSocket] 连接失败: " << errmsg << std::endl;
}

// 连接关闭回调
void OnConnectionClose(int code, const std::string& reason, void* user_data) {
    std::cout << "[WebSocket] 连接关闭: " << code << " - " << reason << std::endl;
}

int main() {
    std::cout << "启动 WebSocket 客户端..." << std::endl;
    
    // 创建客户端
    g_ws_client = CreateWebSocketClient(&g_client_id);
    if (!g_ws_client) {
        std::cout << "创建 WebSocket 客户端失败" << std::endl;
        return -1;
    }
    
    // 配置连接
    g_ws_client->SetConnectUrl("wss://echo.websocket.org/");
    g_ws_client->SetTimeout(10000);  // 10秒超时
    
    // 设置回调
    g_ws_client->SetMessageReceiveCallback(OnMessageReceived, nullptr);
    g_ws_client->SetSuccessCallback(OnConnectSuccess, nullptr);
    g_ws_client->SetFailCallback(OnConnectFail, nullptr);
    g_ws_client->SetCloseCallback(OnConnectionClose, nullptr);
    
    // 连接
    ConnectWebSocket(g_client_id);
    
    // 保持运行
    emscripten_set_main_loop([](){}, 0, 1);
    
    return 0;
}

# 高级配置

#include "websocket/wx_websocket_interface.h"

void CreateAdvancedWebSocket() {
    int client_id;
    auto* client = WXSocket::CreateWebSocketClient(&client_id);
    
    // 设置连接地址
    client->SetConnectUrl("wss://game.example.com/ws");
    
    // 设置超时(毫秒)
    client->SetTimeout(15000);
    
    // 设置 HTTP 请求头
    client->SetHeader("User-Agent", "MyGame/1.0");
    client->SetHeader("Authorization", "Bearer your-token-here");
    client->SetHeader("X-Game-Version", "1.2.3");
    
    // 设置子协议
    std::vector<std::string> protocols = {"game-protocol", "chat"};
    client->SetProtocols(protocols);
    
    // 启用 TCP NoDelay(禁用 Nagle 算法,减少延迟)
    client->SetEnableTcpNoDelay(true);
    
    // 启用消息压缩
    client->SetPerMessageDeflate(true);
    
    // 设置回调
    client->SetMessageReceiveCallback([](const void* msg, int len, void* data) {
        std::cout << "收到: " << std::string((char*)msg, len) << std::endl;
    }, nullptr);
    
    client->SetSuccessCallback([](const auto& header, void* data) {
        std::cout << "连接成功" << std::endl;
    }, nullptr);
    
    client->SetFailCallback([](const std::string& err, void* data) {
        std::cout << "连接失败: " << err << std::endl;
    }, nullptr);
    
    client->SetCloseCallback([](int code, const std::string& reason, void* data) {
        std::cout << "连接关闭: " << code << std::endl;
    }, nullptr);
    
    // 连接
    WXSocket::ConnectWebSocket(client_id);
}

# 封装类示例

#include "websocket/wx_websocket_interface.h"
#include <functional>

class GameWebSocket {
public:
    using MessageHandler = std::function<void(const std::string&)>;
    using ErrorHandler = std::function<void(const std::string&)>;
    
    bool Connect(const std::string& url) {
        client_ = WXSocket::CreateWebSocketClient(&client_id_);
        if (!client_) return false;
        
        client_->SetConnectUrl(url);
        client_->SetTimeout(10000);
        client_->SetEnableTcpNoDelay(true);
        
        // 使用 this 指针传递
        client_->SetMessageReceiveCallback(
            [](const void* msg, int len, void* user_data) {
                auto* self = static_cast<GameWebSocket*>(user_data);
                if (self->on_message_) {
                    self->on_message_(std::string((char*)msg, len));
                }
            }, this);
        
        client_->SetSuccessCallback(
            [](const auto& header, void* user_data) {
                auto* self = static_cast<GameWebSocket*>(user_data);
                self->connected_ = true;
                std::cout << "WebSocket 已连接" << std::endl;
            }, this);
        
        client_->SetFailCallback(
            [](const std::string& err, void* user_data) {
                auto* self = static_cast<GameWebSocket*>(user_data);
                self->connected_ = false;
                if (self->on_error_) {
                    self->on_error_(err);
                }
            }, this);
        
        client_->SetCloseCallback(
            [](int code, const std::string& reason, void* user_data) {
                auto* self = static_cast<GameWebSocket*>(user_data);
                self->connected_ = false;
            }, this);
        
        WXSocket::ConnectWebSocket(client_id_);
        return true;
    }
    
    void Send(const std::string& message) {
        if (connected_) {
            WXSocket::SendWebSocketMessage(client_id_, message.c_str(), message.length());
        }
    }
    
    void SendBinary(const std::vector<uint8_t>& data) {
        if (connected_) {
            WXSocket::SendWebSocketMessage(client_id_, data.data(), data.size());
        }
    }
    
    void Close(int code = 1000, const std::string& reason = "Normal closure") {
        if (client_id_ >= 0) {
            WXSocket::CloseWebSocketClient(client_id_, code, reason);
            connected_ = false;
        }
    }
    
    void OnMessage(MessageHandler handler) { on_message_ = handler; }
    void OnError(ErrorHandler handler) { on_error_ = handler; }
    
    bool IsConnected() const { return connected_; }
    
private:
    int client_id_ = -1;
    WXSocket::WebSocketClientInterface* client_ = nullptr;
    bool connected_ = false;
    MessageHandler on_message_;
    ErrorHandler on_error_;
};

// 使用示例
void UseGameWebSocket() {
    GameWebSocket ws;
    
    ws.OnMessage([](const std::string& msg) {
        std::cout << "游戏消息: " << msg << std::endl;
    });
    
    ws.OnError([](const std::string& err) {
        std::cout << "错误: " << err << std::endl;
    });
    
    ws.Connect("wss://game.example.com/ws");
}

# 关闭码说明

关闭码 含义
1000 正常关闭
1001 端点离开
1002 协议错误
1003 不支持的数据类型
1006 异常关闭(未收到关闭帧)
1007 数据格式错误
1008 策略违规
1009 消息过大
1010 缺少扩展
1011 服务器内部错误

# 常见问题

# Q: 连接失败,提示"未完成的操作"?

A: 检查是否使用了 WSS 协议,微信小游戏真机只支持 WSS,开发者工具支持采用WS进行调试。

# Q: 如何在真机测试?

A: 确保服务器有有效的 SSL 证书,并在微信公众平台配置域名白名单。

# Q: 最多支持多少个连接?

A: 最多 5 个并发 WebSocket 连接。

# 下一步

点击咨询小助手