C SDK 快速接入
华网网络验证提供了官方的 C 语言 SDK,纯 C11 实现,无第三方依赖,方便嵌入式/IoT/桌面应用开发者接入。
接入须知
获取方式
下载地址:https://wwaqe.lanzouq.com/b00odxkahe 密码:b0b5
或者加群839808671在群文件下载SDK,有问题也可在此群交流。
鸣谢
作者:wiki 提供基础验证包源码,由官方调整优化发布
开发环境
| 平台 | 编译器 | 构建工具 | 验证状态 |
|---|---|---|---|
| macOS | Apple Clang (Xcode) | CMake + Ninja/Make | ✅ 通过 |
| Windows | MinGW-w64 GCC 14.2 | CMake + MinGW Makefiles | ✅ 通过 |
| Linux | GCC / Clang | CMake + Make | ✅ 理论兼容 |
前置工具
- CMake ≥ 3.16 下载
- C 编译器:macOS 自带 Xcode Command Line Tools;Windows 安装 MinGW-w64 或 Visual Studio Build Tools
- Windows 额外需要确保
gcc/cmake/mingw32-make在 PATH 中
获取方式
将 hwsdk.h 和 hwsdk.c 放入工程,搭配 CMakeLists.txt:
cmake_minimum_required(VERSION 3.16)
project(my_app C)
set(CMAKE_C_STANDARD 11)
set(CMAKE_C_STANDARD_REQUIRED ON)
add_library(hwsdk STATIC hwsdk.c)
target_include_directories(hwsdk PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
find_package(Threads REQUIRED)
target_link_libraries(hwsdk PUBLIC Threads::Threads)
if(WIN32)
target_link_libraries(hwsdk PUBLIC ws2_32)
endif()
add_executable(my_app main.c)
target_link_libraries(my_app PRIVATE hwsdk)
构建步骤
# macOS
cmake -S . -B build
cmake --build build
# Windows (MinGW)
cmake -S . -B build -G "MinGW Makefiles"
cmake --build build
最小示例
#include "hwsdk.h"
#include <stdio.h>
#ifdef _WIN32
#include <windows.h>
#endif
// 心跳异常回调
static void on_hb_failed(hw_sdk* sdk, const hw_resp* r) {
printf("[HB FAILED] code=%d message=%s\n", r->code, r->message);
if (sdk->login_type == HW_LOGIN_CARD) {
hw_resp lr;
hw_card_login(sdk, &lr);
printf("[RELOGIN] code=%d message=%s\n", lr.code, lr.message);
}
}
int main(void) {
#ifdef _WIN32
SetConsoleOutputCP(CP_UTF8); // Windows 控制台输出中文
#endif
if (!hw_net_init()) {
printf("网络初始化失败\n");
return 1;
}
// 初始化 SDK
hw_sdk sdk;
hw_sdk_init(&sdk, "你的AppKey", "你的AppSecret");
hw_sdk_set_debug(&sdk, 0); // 调试开关,1=开启
hw_sdk_set_retry(&sdk, 9); // 连接失败重试次数
hw_sdk_set_heartbeat_gap(&sdk, 120); // 心跳间隔(秒)
hw_sdk_set_auto_heartbeat(&sdk, 1); // 开启自动心跳
hw_sdk_set_heartbeat_callback(&sdk, on_hb_failed);
// 设置卡密并登录
hw_sdk_set_device_id(&sdk, "设备唯一标识");
hw_sdk_set_card(&sdk, "你的卡密");
hw_resp r;
hw_card_login(&sdk, &r);
printf("[LOGIN] code=%d message=%s\n", r.code, r.message);
if (r.code != 0) {
hw_net_cleanup();
return 1;
}
printf("卡密类型: %s\n", r.cardType);
printf("到期时间: %s\n", r.expires);
printf("Token: %s\n", r.token);
// 启动自动心跳(登录成功后调用)
hw_start_heartbeat(&sdk);
// ========== 你的业务代码 ==========
// ...
// =================================
// 退出时停止心跳并清理
hw_stop_heartbeat(&sdk);
hw_net_cleanup();
return 0;
}
数据结构
hw_resp(响应体)
typedef struct {
int code; // 返回码,0=成功
char message[256]; // 错误信息
char sign[64]; // 响应签名
char token[128]; // 登录令牌
char expires[64]; // 过期时间(格式化)
long long expiresTs; // 过期时间戳(秒)
char config[256]; // 配置内容
long long serverTime; // 服务器时间戳(秒)
char cardType[64]; // 卡密类型,如"年卡""月卡"
char notice[512]; // 软件公告
char value[256]; // 远程变量值
char result[256]; // 远程函数返回值
} hw_resp;
hw_login_type(登录类型)
typedef enum {
HW_LOGIN_NONE = 0, // 未登录
HW_LOGIN_CARD = 1, // 卡密登录
HW_LOGIN_USER = 2, // 用户登录
HW_LOGIN_TRIAL = 3, // 试用登录
} hw_login_type;
初始化
初始化 SDK
void hw_sdk_init(hw_sdk* s, const char* appKey, const char* appSecret);
初始化卡密
void hw_sdk_set_card(hw_sdk* s, const char* card);
| 参数 | 必传 | 类型 | 说明 |
|---|---|---|---|
| card | 是 | const char* | 卡密(长度不超过45位) |
初始化用户账号
void hw_sdk_set_user(hw_sdk* s, const char* username, const char* password);
| 参数 | 必传 | 类型 | 说明 |
|---|---|---|---|
| username | 是 | const char* | 用户名(1-20位) |
| password | 是 | const char* | 密码(6-30位) |
初始化设备标识
void hw_sdk_set_device_id(hw_sdk* s, const char* deviceId);
| 参数 | 必传 | 类型 | 说明 |
|---|---|---|---|
| deviceId | 是 | const char* | 设备唯一标识码 |
初始化心跳回调
void hw_sdk_set_heartbeat_callback(hw_sdk* s, hw_heartbeat_failed_cb cb);
回调函数签名:void (*hw_heartbeat_failed_cb)(hw_sdk* sdk, const hw_resp* r);
当心跳返回 code ≠ 0 或网络请求失败时触发。
卡密
卡密登录
登录成功后将自动把 login_type 设为 HW_LOGIN_CARD,token 写入 sdk.token。
int hw_card_login(hw_sdk* s, hw_resp* out);
// 用前需设置
hw_sdk_set_device_id(&sdk, "设备ID");
hw_sdk_set_card(&sdk, "卡密");
| 返回值 | 说明 |
|---|---|
| 1 | 请求完成(具体结果看 out->code,0=成功) |
| 0 | 网络传输失败 |
// 示例
hw_resp r;
hw_card_login(&sdk, &r);
if (r.code == 0) {
printf("登录成功: cardType=%s expires=%s\n", r.cardType, r.expires);
hw_start_heartbeat(&sdk); // 启动自动心跳
} else {
printf("登录失败: %s\n", r.message);
}
卡密心跳
int hw_card_heartbeat(hw_sdk* s, hw_resp* out);
一般无需手动调用,hw_start_heartbeat 会按 heartbeat_gap_sec 间隔自动发送。
卡密退出登录
int hw_card_logout(hw_sdk* s, hw_resp* out);
// 示例
hw_resp r;
hw_card_logout(&sdk, &r);
printf("退出 code=%d message=%s\n", r.code, r.message);
hw_stop_heartbeat(&sdk); // 停止心跳线程
卡密解绑设备
需先在开发者后台配置"开启设备绑定且可解除绑定"
int hw_card_unbind_device(hw_sdk* s, hw_resp* out);
// 示例(登录后调用)
hw_resp r;
hw_card_unbind_device(&sdk, &r);
printf("解绑 code=%d message=%s\n", r.code, r.message);
卡密设置解绑密码
用于设备丢失时通过密码解绑
int hw_card_set_unbind_password(hw_sdk* s, const char* password, hw_resp* out);
| 参数 | 必传 | 类型 | 说明 |
|---|---|---|---|
| password | 是 | const char* | 解绑密码(6-10位) |
// 示例
hw_resp r;
hw_card_set_unbind_password(&sdk, "MyPwd123", &r);
printf("设置解绑密码 code=%d message=%s\n", r.code, r.message);
卡密通过密码解绑设备
int hw_card_unbind_by_password(hw_sdk* s, const char* password, hw_resp* out);
| 参数 | 必传 | 类型 | 说明 |
|---|---|---|---|
| password | 是 | const char* | 解绑密码(6-10位) |
// 示例(无需登录即可调用)
hw_sdk_init(&sdk, "appKey", "appSecret");
hw_sdk_set_card(&sdk, "被解绑的卡密");
hw_resp r;
hw_card_unbind_by_password(&sdk, "MyPwd123", &r);
printf("密码解绑 code=%d message=%s\n", r.code, r.message);
卡密充值(以卡充卡)
int hw_card_recharge(hw_sdk* s, const char* useCard, hw_resp* out);
| 参数 | 必传 | 类型 | 说明 |
|---|---|---|---|
| useCard | 是 | const char* | 充值使用的卡密(不超过45位) |
// 示例(登录后调用)
hw_resp r;
hw_card_recharge(&sdk, "充值卡密", &r);
printf("充值 code=%d message=%s\n", r.code, r.message);
用户
用户登录
登录成功后将自动把 login_type 设为 HW_LOGIN_USER,token 写入 sdk.token。
int hw_user_login(hw_sdk* s, hw_resp* out);
// 用前需设置
hw_sdk_set_user(&sdk, "用户名", "密码");
// 示例
hw_resp r;
hw_user_login(&sdk, &r);
if (r.code == 0) {
printf("登录成功: token=%s expires=%s\n", r.token, r.expires);
hw_start_heartbeat(&sdk);
}
用户心跳
int hw_user_heartbeat(hw_sdk* s, hw_resp* out);
一般无需手动调用,自动心跳线程会根据 login_type 自动选择。
用户退出登录
int hw_user_logout(hw_sdk* s, hw_resp* out);
用户注册(通过卡密)
int hw_user_register(hw_sdk* s, const char* card, const char* password, hw_resp* out);
| 参数 | 必传 | 类型 | 说明 |
|---|---|---|---|
| card | 是 | const char* | 注册使用的卡密(不超过45位) |
| password | 是 | const char* | 用户密码(6-30位) |
// 示例:先设置用户名,再调用注册
hw_sdk_set_user(&sdk, "新用户名", "");
hw_resp r;
hw_user_register(&sdk, "注册卡密", "用户密码", &r);
printf("注册 code=%d message=%s\n", r.code, r.message);
用户修改密码
int hw_user_set_password(hw_sdk* s, const char* oldPassword,
const char* newPassword, hw_resp* out);
| 参数 | 必传 | 类型 | 说明 |
|---|---|---|---|
| oldPassword | 是 | const char* | 当前密码 |
| newPassword | 是 | const char* | 新密码(6-30位) |
// 示例(登录后调用)
hw_resp r;
hw_user_set_password(&sdk, "旧密码", "新密码", &r);
printf("改密 code=%d message=%s\n", r.code, r.message);
用户充值(通过卡密)
int hw_user_recharge(hw_sdk* s, const char* card, hw_resp* out);
| 参数 | 必传 | 类型 | 说明 |
|---|---|---|---|
| card | 是 | const char* | 充值使用的卡密(不超过45位) |
// 示例(登录后调用)
hw_resp r;
hw_user_recharge(&sdk, "充值卡密", &r);
printf("充值 code=%d message=%s\n", r.code, r.message);
试用
试用登录
登录成功后将自动把 login_type 设为 HW_LOGIN_TRIAL。
int hw_trial_login(hw_sdk* s, hw_resp* out);
// 用前需设置
hw_sdk_set_device_id(&sdk, "设备ID");
// 示例
hw_resp r;
hw_trial_login(&sdk, &r);
if (r.code == 0) {
printf("试用登录成功 expires=%s expiresTs=%lld\n", r.expires, r.expiresTs);
hw_start_heartbeat(&sdk);
}
试用心跳
int hw_trial_heartbeat(hw_sdk* s, hw_resp* out);
一般无需手动调用,自动心跳线程会根据 login_type 自动选择。
配置
获取卡密配置
int hw_card_get_config(hw_sdk* s, hw_resp* out);
// 用前需设置
hw_sdk_set_card(&sdk, "卡密");
// 示例
hw_resp r;
hw_card_get_config(&sdk, &r);
printf("卡密配置: code=%d config=%s\n", r.code, r.config);
设置卡密配置
int hw_card_set_config(hw_sdk* s, const char* config, hw_resp* out);
| 参数 | 必传 | 类型 | 说明 |
|---|---|---|---|
| config | 是 | const char* | 配置内容(不超过512位) |
// 示例
hw_resp r;
hw_card_set_config(&sdk, "我的自定义配置", &r);
printf("设置配置 code=%d\n", r.code);
获取用户配置
int hw_user_get_config(hw_sdk* s, hw_resp* out);
// 用前需设置
hw_sdk_set_user(&sdk, "用户名", "密码");
设置用户配置
int hw_user_set_config(hw_sdk* s, const char* config, hw_resp* out);
// 示例
hw_resp r;
hw_user_set_config(&sdk, "用户专属配置", &r);
printf("设置用户配置 code=%d\n", r.code);
软件
获取软件配置
int hw_software_get_config(hw_sdk* s, hw_resp* out);
// 示例
hw_resp r;
hw_software_get_config(&sdk, &r);
printf("软件配置: code=%d config=%s\n", r.code, r.config);
获取软件公告
int hw_software_get_notice(hw_sdk* s, hw_resp* out);
// 示例
hw_resp r;
hw_software_get_notice(&sdk, &r);
printf("软件公告: code=%d notice=%s\n", r.code, r.notice);
获取软件最新版本
该接口不参与 sign 计算
int hw_software_get_version(hw_sdk* s, const char* version, hw_resp* out);
| 参数 | 必传 | 类型 | 说明 |
|---|---|---|---|
| version | 是 | const char* | 当前本地版本号 |
// 示例
hw_resp r;
hw_software_get_version(&sdk, "1.0.0", &r);
printf("版本检查: code=%d\n", r.code);
// code=0 表示有新版本,code≠0 已是最新
远程变量
创建远程变量
int hw_senior_add_var(hw_sdk* s, const char* key,
const char* value, hw_resp* out);
| 参数 | 必传 | 类型 | 说明 |
|---|---|---|---|
| key | 是 | const char* | 变量名(不超过64位) |
| value | 是 | const char* | 变量值(不超过256位) |
// 示例
hw_resp r;
hw_senior_add_var(&sdk, "my_key", "hello", &r);
printf("创建变量 code=%d message=%s\n", r.code, r.message);
获取远程变量
int hw_senior_get_var(hw_sdk* s, const char* key, hw_resp* out);
// 示例
hw_resp r;
hw_senior_get_var(&sdk, "my_key", &r);
printf("获取变量: code=%d value=%s\n", r.code, r.value);
修改远程变量
int hw_senior_set_var(hw_sdk* s, const char* key,
const char* value, hw_resp* out);
// 示例
hw_resp r;
hw_senior_set_var(&sdk, "my_key", "updated_value", &r);
printf("修改变量 code=%d\n", r.code);
删除远程变量
int hw_senior_del_var(hw_sdk* s, const char* key, hw_resp* out);
// 示例
hw_resp r;
hw_senior_del_var(&sdk, "my_key", &r);
printf("删除变量 code=%d\n", r.code);
远程函数
调用远程函数
int hw_senior_call_fun(hw_sdk* s, const char* funName,
const char* params, hw_resp* out);
| 参数 | 必传 | 类型 | 说明 |
|---|---|---|---|
| funName | 是 | const char* | 远程函数名(不超过64位) |
| params | 否 | const char* | JSON 列表字符串,如 "[10, 20]"(不超过256位) |
示例:假设开发后台定义了一个远程函数 add:
function add(a, b) {
return a + b;
}
// C 端调用
hw_resp r;
hw_senior_call_fun(&sdk, "add", "[10, 20]", &r);
if (r.code == 0) {
printf("add(10,20) = %s\n", r.result); // 输出: add(10,20) = 30
} else {
printf("调用失败: %s\n", r.message);
}
心跳管理
启动自动心跳
登录成功后调用,SDK 根据 login_type 自动选择卡密/用户/试用心跳接口。重复调用会自动停止旧线程再启新线程。
void hw_start_heartbeat(hw_sdk* s);
停止自动心跳
void hw_stop_heartbeat(hw_sdk* s);
心跳配置
// 设置心跳间隔(秒),默认 120,请勿低于 60
hw_sdk_set_heartbeat_gap(&sdk, 120);
// 开启/关闭自动心跳,默认开启
hw_sdk_set_auto_heartbeat(&sdk, 1);
// 心跳异常回调
hw_sdk_set_heartbeat_callback(&sdk, on_hb_failed);
其他配置
调试开关
hw_sdk_set_debug(&sdk, 1); // 开启后输出每次请求的详细日志到 stderr
hw_sdk_set_debug(&sdk, 0); // 关闭
重试次数
连接失败时的重试次数(Fibonacci 间隔),默认 9 次。
hw_sdk_set_retry(&sdk, 9);
自定义服务器地址
const char* hosts[] = {"api.hwwlyz.com", "api3.hwwlyz.com", "api6.hwwlyz.com"};
hw_sdk_set_hosts(&sdk, hosts, 3);
网络初始化与清理
int hw_net_init(void); // 程序启动时调用,Windows 上初始化 WinSock
void hw_net_cleanup(void); // 程序退出时调用
返回码对照表
| 返回码 | 说明 |
|---|---|
| 0 | 成功 |
| -1 | SDK 内部:连接服务器失败 |
| -2 | SDK 内部:响应签名校验失败 |
| -4 | SDK 内部:响应时间超时 |
| 51 | 参数错误 |
| 10101 | 签名已过期(60s 有效且一次性) |
| 10102 | 时间戳大于服务器时间 |
| 10103 | appKey/appSecret 不匹配 |
| 10104 | 无效签名 |
| 10105 | 账户欠费停用 |
| 10201 | 软件不存在 |
| 10202 | 卡密不可用 |
| 10203 | 卡密已冻结 |
| 10204 | 卡密已过期 |
| 10205 | 卡密已被使用 |
| 10206 | 超过多开上限 |
| 10207 | 登录状态已失效 |
| 10208 | 超出可绑定设备上限 |
| 10209 | 解绑密码不正确 |
| 10301 | 账号已存在 |
| 10302 | 用户密码错误 |
| 10303 | 用户已到期 |
| 10401 | 远程变量不存在 |
| 10402 | 远程变量不支持 API 修改 |
| 10403 | 远程变量名已存在 |
| 10501 | 远程函数不存在 |
| 10601 | 已是最新版本 |
| 10701 | 软件未开启试用 |
| 10702 | 本次试用已到期 |
| 10703 | 试用已到期(一次性试用) |
| 10704 | 本周期试用已到期(间隔试用) |
| 10705 | 请先登录 |
| 10706 | 设备正在试用,请勿重复登录 |
