本文档介绍如何在 Emscripten 项目中集成微信小游戏 HTTP 客户端模块,适用于Cocos2dx、自研引擎项目。
概述
本模块提供完整的 HTTP 客户端实现,将 C++ 的 HTTP 请求映射到微信小游戏的 wx.request、wx.downloadFile、wx.uploadFile API。
业务只需要:
- 创建 HTTP 客户端对象
- 配置请求参数
- 设置成功/失败回调
- 发送请求
功能特性
| 功能 |
说明 |
| HTTP 请求 |
GET, POST, PUT, DELETE, HEAD, OPTIONS 等方法 |
| 文件下载 |
支持下载文件到本地路径 |
| 文件上传 |
支持上传本地文件到服务器 |
| HTTPS |
支持安全连接 |
| 请求头 |
自定义 HTTP 请求头 |
| 请求体 |
支持文本和二进制数据 |
| 响应处理 |
支持文本和二进制响应 |
| 超时控制 |
可配置请求超时时间 |
| 高级特性 |
HTTP/2、QUIC、HttpDNS、缓存等 |
快速开始
集成到项目
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='[\
\"_HttpRequestOnCookie\", \
\"_HttpRequestOnSuccessStrData\", \
\"_HttpRequestOnSuccessBinData\", \
\"_HttpOnError\", \
\"_main\", \"_free\", \"_malloc\"]' \
--js-library ${WXGAMESDK_DIR}/jslib/libwxhttp_open.jslib \
-Wl,--whole-archive ${WXGAMESDK_DIR}/lib/libwxgamesdk.a -Wl,--no-whole-archive"
)
API 参考
头文件
#include "http/wx_http_interface.h"
命名空间
所有 HTTP 相关接口都在 WXSocket 命名空间下。
核心接口
1. HTTP 请求客户端
WXSocket::HttpClientInterface* CreateHttpClient(int* client_id);
int SendHttpRequest(int client_id);
int AbortHttpRequest(int client_id);
2. 文件下载客户端
WXSocket::HttpDownloadClientInterface* CreateHttpDownloadClient(int* client_id);
int SendHttpDownloadRequest(int client_id);
int AbortHttpDownloadRequest(int client_id);
3. 文件上传客户端
WXSocket::HttpUploadClientInterface* CreateHttpUploadClient(int* client_id);
int SendHttpUploadRequest(int client_id);
int AbortHttpUploadRequest(int client_id);
响应数据结构
namespace WXSocket {
struct Response {
std::string version;
int statusCode;
std::string reason;
Headers headers;
std::vector<std::string> cookies;
bool useHttpDns;
};
struct Error {
int errorCode;
std::string errorMsg;
bool useHttpDns;
};
struct HttpRequestProgress {
int progress;
int totalBytesExpected;
int totalBytes;
};
}
使用示例
基本 HTTP 请求
#include "http/wx_http_interface.h"
#include <cstdio>
struct HttpTest {
std::string method;
bool http_request_success = false;
bool http_request_complete = false;
bool http_request_fail = false;
int client_id = 0;
bool IsComplete() { return http_request_complete; }
void CheckSuccess() { assert(http_request_success); }
};
void OnSuccess(const WXSocket::Response& response, const void* data, size_t size, void* user_data) {
HttpTest* test = (HttpTest*)user_data;
printf("HTTP 请求成功, id:%d, method:%s, data:%s\n",
test->client_id, test->method.c_str(), (const char*)data);
test->http_request_success = true;
}
void OnFail(const WXSocket::Error& error, void* user_data) {
HttpTest* test = (HttpTest*)user_data;
printf("HTTP 请求失败, id:%d, method:%s, error:%d, msg:%s\n",
test->client_id, test->method.c_str(), error.errorCode, error.errorMsg.c_str());
test->http_request_fail = true;
}
void OnComplete(void* user_data) {
HttpTest* test = (HttpTest*)user_data;
printf("HTTP 请求完成, id:%d, method:%s\n", test->client_id, test->method.c_str());
test->http_request_complete = true;
}
WXSocket::HttpClientInterface* ConstructHttpRequest(
HttpTest* test,
const std::string& url,
const std::string& method,
const std::string& body) {
int client_id = 0;
WXSocket::HttpClientInterface* httpclient = WXSocket::CreateHttpClient(&client_id);
if (httpclient == nullptr) {
return nullptr;
}
httpclient->SetUrl(url);
httpclient->SetMethod(method);
httpclient->SetTimeout(2000);
httpclient->SetResponseType(WXSocket::HttpClientInterface::kResponseTypeString);
httpclient->SetBody(WXSocket::HttpClientInterface::kDataTypeString, body.c_str(), body.size());
httpclient->SetHeader("Content-Type", "application/json");
test->client_id = client_id;
test->method = method;
httpclient->SetSuccessCallback(OnSuccess, test);
httpclient->SetFailCallback(OnFail, test);
httpclient->SetCompleteCallback(OnComplete, test);
return httpclient;
}
void TestHttpRequest() {
static HttpTest httptest_get;
static HttpTest httptest_post;
auto* client = ConstructHttpRequest(&httptest_get, "https://api.example.com/users", "GET", "");
if (client) {
int ret = WXSocket::SendHttpRequest(httptest_get.client_id);
assert(ret == 0);
}
std::string post_data = R"({"id": 3, "name": "tom", "email": "tom@example.com"})";
client = ConstructHttpRequest(&httptest_post, "https://api.example.com/users", "POST", post_data);
if (client) {
int ret = WXSocket::SendHttpRequest(httptest_post.client_id);
assert(ret == 0);
}
}
文件下载
#include "http/wx_http_interface.h"
#include <cstdio>
struct HttpTest {
int client_id = 0;
bool http_request_success = false;
bool http_request_complete = false;
};
void OnDownloadSuccess(const WXSocket::HttpDownloadClientInterface::HttpDownloadResponse& response, void* user_data) {
HttpTest* test = (HttpTest*)user_data;
printf("下载成功, id:%d, status_code:%d, tempFilePath:%s, filePath:%s\n",
test->client_id, response.statusCode,
response.tempFilePath.c_str(), response.filePath.c_str());
test->http_request_success = true;
FILE* fp = fopen(response.filePath.c_str(), "r");
if (fp) {
char buffer[1024];
size_t readsize = fread(buffer, 1, sizeof(buffer), fp);
printf("读取内容: %.*s\n", (int)readsize, buffer);
fclose(fp);
}
}
void OnDownloadFail(const WXSocket::Error& error, void* user_data) {
HttpTest* test = (HttpTest*)user_data;
printf("下载失败, id:%d, error:%d, msg:%s\n",
test->client_id, error.errorCode, error.errorMsg.c_str());
}
void OnDownloadComplete(void* user_data) {
HttpTest* test = (HttpTest*)user_data;
test->http_request_complete = true;
printf("下载完成, id:%d\n", test->client_id);
}
WXSocket::HttpDownloadClientInterface* ConstructHttpDownloadRequest(
HttpTest* test,
const std::string& url,
const std::string& filePath) {
int client_id = 0;
WXSocket::HttpDownloadClientInterface* httpclient = WXSocket::CreateHttpDownloadClient(&client_id);
if (httpclient == nullptr) {
return nullptr;
}
httpclient->SetUrl(url);
httpclient->SetFilePath(filePath);
httpclient->SetTimeout(60000);
test->client_id = client_id;
httpclient->SetSuccessCallback(OnDownloadSuccess, test);
httpclient->SetFailCallback(OnDownloadFail, test);
httpclient->SetCompleteCallback(OnDownloadComplete, test);
return httpclient;
}
void TestDownload() {
static HttpTest httptest_download;
const std::string url = "https://example.com/config/settings.json";
const std::string localPath = "/CustomWritablePath/Library/Application_Support/1.0.0/remoteSettings.json";
auto* client = ConstructHttpDownloadRequest(&httptest_download, url, localPath);
if (client) {
int ret = WXSocket::SendHttpDownloadRequest(httptest_download.client_id);
assert(ret == 0);
printf("下载请求已发送\n");
}
}
文件上传
#include "http/wx_http_interface.h"
#include <cstdio>
struct HttpTest {
int client_id = 0;
bool http_request_success = false;
};
void OnUploadSuccess(const WXSocket::HttpUploadClientInterface::HttpUploadResponse& response,
const char* data, void* user_data) {
HttpTest* test = (HttpTest*)user_data;
printf("上传成功, id:%d, status_code:%d, response:%s\n",
test->client_id, response.statusCode, data ? data : "");
test->http_request_success = true;
}
void OnUploadFail(const WXSocket::Error& error, void* user_data) {
HttpTest* test = (HttpTest*)user_data;
printf("上传失败, id:%d, error:%d, msg:%s\n",
test->client_id, error.errorCode, error.errorMsg.c_str());
}
void OnUploadComplete(void* user_data) {
HttpTest* test = (HttpTest*)user_data;
printf("上传完成, id:%d\n", test->client_id);
}
WXSocket::HttpUploadClientInterface* ConstructHttpUploadRequest(
HttpTest* test,
const std::string& url,
const std::string& filePath) {
int client_id = 0;
WXSocket::HttpUploadClientInterface* httpclient = WXSocket::CreateHttpUploadClient(&client_id);
if (httpclient == nullptr) {
return nullptr;
}
httpclient->SetUrl(url);
httpclient->SetFilePath(filePath);
httpclient->SetFileName("file");
httpclient->SetTimeout(30000);
test->client_id = client_id;
httpclient->SetSuccessCallback(OnUploadSuccess, test);
httpclient->SetFailCallback(OnUploadFail, test);
httpclient->SetCompleteCallback(OnUploadComplete, test);
return httpclient;
}
void TestUpload() {
static HttpTest httptest_upload;
FILE* fp = fopen("/CustomWritablePath/upload_test.txt", "w");
fprintf(fp, "hello world");
fclose(fp);
auto* client = ConstructHttpUploadRequest(
&httptest_upload,
"https://api.example.com/upload",
"/CustomWritablePath/upload_test.txt");
if (client) {
int ret = WXSocket::SendHttpUploadRequest(httptest_upload.client_id);
assert(ret == 0);
}
}
主循环中处理请求完成
#include <emscripten.h>
static HttpTest httptest_download;
void main_loop() {
if (httptest_download.http_request_complete) {
if (httptest_download.http_request_success) {
printf("下载成功完成\n");
}
if (httptest_download.client_id != 0) {
int ret = WXSocket::AbortHttpDownloadRequest(httptest_download.client_id);
assert(ret == 0);
httptest_download.client_id = 0;
}
}
}
int main() {
auto* client = ConstructHttpDownloadRequest(
&httptest_download,
"https://example.com/config.json",
"/CustomWritablePath/config.json");
if (client) {
WXSocket::SendHttpDownloadRequest(httptest_download.client_id);
}
emscripten_set_main_loop(main_loop, 0, 0);
return 0;
}
HttpClientInterface 详细配置
必选配置
| 方法 |
说明 |
SetUrl(url) |
设置请求 URL |
SetMethod(method) |
设置 HTTP 方法(GET/POST/PUT/DELETE 等) |
SetSuccessCallback(callback, user_data) |
设置成功回调 |
SetFailCallback(callback, user_data) |
设置失败回调 |
常用配置
| 方法 |
说明 |
SetTimeout(ms) |
设置超时时间(毫秒),默认 60000 |
SetHeader(key, value) |
设置请求头 |
SetBody(type, data, size) |
设置请求体(文本或二进制) |
SetResponseType(type) |
设置响应类型(kResponseTypeString / kResponseTypeBinary) |
SetCompleteCallback(callback, user_data) |
设置完成回调(可选) |
高级配置
| 方法 |
说明 |
SetEnableHttp2(enable) |
启用 HTTP/2 协议 |
SetEnableQuic(enable) |
启用 QUIC/H3 协议 |
SetEnableHttpDNS(enable) |
启用 HttpDNS |
SetHttpDNSServiceId(serviceId) |
设置 HttpDNS 服务商 ID |
SetEnableCache(enable) |
启用 HTTP 缓存 |
SetEnableChunked(enable) |
启用分块传输 |
SetUseHighPerformanceMode(enable) |
启用高性能模式 |
SetHeaderReceivedCallback(callback, user_data) |
设置响应头接收回调 |
SetChunkReceivedCallback(callback, user_data) |
设置分块数据接收回调 |
HttpDownloadClientInterface 配置
| 方法 |
说明 |
SetUrl(url) |
下载 URL |
SetFilePath(path) |
本地保存路径(必须以 /CustomWritablePath 开头) |
SetHeader(key, value) |
请求头 |
SetTimeout(ms) |
超时时间 |
SetSuccessCallback |
成功回调,返回 HttpDownloadResponse |
SetFailCallback |
失败回调 |
SetProgressUpdateCallback |
进度回调(可选) |
HttpUploadClientInterface 配置
| 方法 |
说明 |
SetUrl(url) |
上传服务器 URL |
SetFilePath(path) |
本地文件路径 |
SetFileName(name) |
文件对应的 key(服务端用此 key 获取文件) |
SetHeader(key, value) |
请求头 |
SetFormData(data) |
额外的表单数据 |
SetTimeout(ms) |
超时时间 |
SetSuccessCallback |
成功回调,返回 HttpUploadResponse |
SetFailCallback |
失败回调 |
SetProgressUpdateCallback |
进度回调(可选) |
错误处理
错误码说明
| 错误码 |
说明 |
| 0 |
成功 |
| -1 |
通用错误 / 请求失败 |
HTTP 状态码
| 状态码 |
说明 |
| 200 |
成功 |
| 201 |
创建成功 |
| 400 |
请求错误 |
| 401 |
未授权 |
| 403 |
禁止访问 |
| 404 |
未找到 |
| 500 |
服务器内部错误 |
注意事项
- 域名配置:生产环境需要在微信公众平台配置合法域名
- 本地路径:下载文件路径必须以
/CustomWritablePath 开头
- 资源释放:请求完成后,SDK 会自动清理资源;如需提前终止请求,调用对应的
Abort 方法
- 回调线程:回调函数在主线程中执行,避免在回调中执行耗时操作
- 并发限制:注意控制同时发起的请求数量
下一步