简体中文

基于P2PTunnelAPIs和TCPIP协议开发NAS(或者摄像头)-设备端

P2PTunnelAPIs 开发指南 | TUTK P2P SDK 开发手册

[开发指南] P2PTunnelAPIs 核心开发手册(TCP/IP协议适配)

Version: 适配SDK全版本 | 适用场景: 外网访问内网TCP/IP服务 | 核心特性: 虚拟隧道、端口映射、多协议兼容、无需公网IP

一、导语

TUTK 提供的 P2PTunnel 服务,类似于 VPN 服务。P2PTunnel 服务启动后,将通过 TUTK 的私有协议,将上层传入的数据转发到对端,而且不需要知道对端的IP。
P2PTunnel 模块,可以内嵌至厂商的程序内,也可以独立做成一个模块。对于一些基于 TCP/IP 的标准或者私有服务,比如 HTTP、SSH、FTP、Telnet、RTSP,只需要简单几行代码,就可以完成接入。

二、核心工作原理

P2PTunnel 核心是建立外网与内网设备的虚拟数据隧道,通过 TUTK 服务器进行设备发现和连接撮合,无需路由器端口转发。数据传输时,上层 TCP/IP 服务的数据包通过隧道透明转发,对原有服务无侵入性修改。
2.1 P2PTunnel 模块工作示意图

基于P2PTunnelAPIs和TCPIP协议开发NAS-device-1.png

图 1:P2PTunnel 模块与上层TCP/IP服务的交互逻辑
2.2 P2PTunnel Server 使用流程图

截图.png

图 2:P2PTunnel Server 初始化、启动、数据传输完整流程

三、核心开发步骤(Server端)

以下为 P2PTunnel Server 端的核心开发流程,包含初始化、配置、启动、回调实现、反初始化等关键步骤,适配 SDK 全版本。
3.1 SDK 初始化(必选)

步骤1设置 SDK 许可证密钥

首先需通过 TUTK 提供的许可证密钥激活 SDK,否则后续接口调用会失败。
// 设置SDK许可证密钥(由 TUTK 提供)
int ret = TUTK_SDK_Set_License_Key(sdk_license_key);
if (ret != TUTK_ER_NoERROR) {
    printf("TUTK_SDK_Set_License_Key() error[%d]!\n", ret);
    return -1;
}
说明:sdk_license_key 为 TUTK 官方提供的合法密钥,需妥善保管,避免泄露。

步骤2初始化 P2P 隧道服务器

根据 SDK 版本选择对应的初始化接口,新版本支持局域网直连模式(提升传输速度)。
// 初始化 P2P 隧道服务器(根据 SDK 版本选择接口)
#if _USE_SDK_VERSION_BELOW_4_3_5_0_
// 旧版本初始化接口(SDK 版本 < 4.3.5.0)
// 参数:允许的最大访客连接数(自定义,如8、16等)
#define MAX_CONNECTION 8  // 最大连接数示例
ret = P2PTunnelServerInitialize(MAX_CONNECTION);
if (ret != TUNNEL_ER_NoERROR) {
    printf("P2PTunnelServerInitialize() error[%d]!\n", ret);
    return -1;
}
#else
// 新版本初始化接口(SDK 版本 >= 4.3.5.0)
// 参数1:允许的最大访客连接数
// 参数2:1=开启局域网直连(提升速度,局域网内数据不加密);0=关闭
// 注意:客户端需同样设置为1才能启用直连模式,否则无效
ret = P2PTunnelServerInitialize2(MAX_CONNECTION, 1);
if (ret != TUNNEL_ER_NoERROR) {
    printf("P2PTunnelServerInitialize2() error[%d]!\n", ret);
    return -1;
}
#endif
说明:初始化接口仅需调用一次(程序启动时),多次调用可能导致资源冲突。

步骤3注册连接状态回调函数

注册回调函数以监听访客连接状态变更(如连接建立、断开),便于业务层处理。
// 注册连接状态回调函数
// 功能:当访客连接状态变更时,通过 TunnelStatusCB 回调通知业务层
// 参数1:回调函数指针(需自定义实现)
// 参数2:自定义参数(传递给回调函数,如设备上下文信息)
P2PTunnelServer_GetStatus(TunnelStatusCB, (void *)args);
说明:TunnelStatusCB 为自定义回调函数,需按 SDK 定义的函数原型实现,示例见后续回调章节。
3.2 端口白名单配置(可选)

步骤1实现端口验证回调函数

通过回调函数控制允许访问的端口,实现端口级别的访问控制,提升安全性(如仅开放 HTTP 80、SSH 22 端口)。
/**
 * P2P隧道端口验证回调函数
 * @brief 控制哪些端口允许通过隧道进行访问,实现端口级别的访问控制
 * @param nServicePort 待验证的服务端口号(如22、80等)
 * @param pArg 自定义参数(注册时传入,当前可传NULL)
 * @return 0=允许访问该端口,-1=拒绝访问该端口
 */
#define WEB_SERVICE_PORT    80    // Web服务端口(HTTP)
#define SSH_SERVICE_PORT    22    // SSH/SFTP服务端口
#define TELNET_SERVICE_PORT 23    // Telnet服务端口

int TunnelPortVerifyCB(uint16_t nServicePort, const void *pArg) {
    // 根据端口号判断是否允许访问
    switch (nServicePort) {
        case WEB_SERVICE_PORT:
        case SSH_SERVICE_PORT:
        case TELNET_SERVICE_PORT:
            // 允许访问预设的合法服务端口
            printf("[%s] 允许访问端口: %d\n", __func__, nServicePort);
            return 0;
        
        default:
            // 拒绝访问未授权的端口,防止非法访问
            printf("[%s] 拒绝访问端口: %d\n", __func__, nServicePort);
            return -1;
    }
}

步骤2注册端口验证回调

调用 SDK 接口注册回调函数,启用端口访问控制功能。
// 注册端口验证回调函数,实现端口访问控制
int ret = P2PTunnelServer_Register_Port_Verify(TunnelPortVerifyCB, NULL);
if (ret != TUNNEL_ER_NoERROR) {
    printf("P2PTunnelServer_Register_Port_Verify() 注册失败,错误码: %d\n", ret);
    return -1;
} else {
    printf("P2PTunnelServer_Register_Port_Verify() 注册成功,已启用端口访问控制\n");
}
说明:若无需端口控制,可跳过该步骤,SDK 默认允许所有端口访问。
3.3 启动 P2PTunnel Server(必选)

步骤1调用启动接口

传入设备 UID、认证回调、会话信息回调等参数,启动隧道服务器,开始监听客户端连接。
// 启动P2P隧道服务器(扩展接口,支持认证和会话信息回调)
// 参数说明:
// - gUid:服务器唯一标识(设备UID,由TUTK分配或自定义,需确保全局唯一)
// - TunnelServerAuthentication:客户端认证回调函数(验证账号密码合法性)
// - TunnelSessionInfoExCB:会话信息回调函数(获取连接模式、NAT类型等)
// - args:自定义参数(传递给回调函数,如设备上下文)
int ret = P2PTunnelServer_Start_Ex(gUid, TunnelServerAuthentication, TunnelSessionInfoExCB, args);

// 处理启动结果
if (ret != TUNNEL_ER_NoERROR) {
    // 启动失败:输出错误码便于排查(错误码含义参考SDK文档)
    printf("P2PTunnelServer_Start_Ex() 启动失败,错误码: %d\n", ret);
    return -1;
} else {
    // 启动成功:设备可通过互联网接收客户端连接
    printf("P2PTunnelServer_Start_Ex() 启动成功,设备UID: %s,等待客户端连接...\n", gUid);
}
说明:gUid 为设备唯一标识,客户端需通过该 UID 定位目标设备,建议使用 TUTK 分配的官方 UID。
3.4 核心回调函数实现(必选)

回调1、客户端账号密码认证回调

验证客户端传入的账号合法性,并返回对应密码供 SDK 校验,实现访问权限控制。
/**
 * P2P隧道服务器端认证回调函数
 * @brief 验证客户端账号合法性,并返回对应密码供SDK验证
 * @param cszAccount 客户端提供的账号(由客户端传入)
 * @param cszPassword 输出参数:填充账号对应密码的缓冲区(SDK用于校验)
 * @param nPasswordMaxLength cszPassword缓冲区的最大长度(含字符串终止符'\0')
 * @param pArg 自定义参数(启动函数传入的args)
 */
// 预设合法账号密码(实际场景建议从配置文件或云端读取)
#define TUNNEL_USERNAME "tutk_tunnel"  // 合法账号
#define TUNNEL_PASSWORD "12345678"     // 对应密码

void TunnelServerAuthentication(
    const char *cszAccount, 
    char *cszPassword, 
    uint32_t nPasswordMaxLength, 
    const void *pArg
) {
    // 打印认证日志(生产环境建议移除明文账号,避免安全风险)
    printf("[%s] 客户端认证请求 - 账号: %s, 密码缓冲区大小: %d\n",
           __func__, cszAccount ? cszAccount : "NULL", nPasswordMaxLength);

    // 校验输入参数有效性(避免空指针异常)
    if (cszAccount == NULL || cszPassword == NULL || nPasswordMaxLength == 0) {
        printf("[%s] 无效认证参数(账号/密码缓冲区为空),拒绝连接\n", __func__);
        return;
    }

    // 验证客户端账号是否合法
    if (strcmp(cszAccount, TUNNEL_USERNAME) == 0) {
        // 账号匹配:安全填充密码(使用strncpy避免缓冲区溢出)
        if (TUNNEL_PASSWORD != NULL) {
            strncpy(cszPassword, TUNNEL_PASSWORD, nPasswordMaxLength - 1);
            cszPassword[nPasswordMaxLength - 1] = '\0';  // 强制添加字符串终止符
            printf("[%s] 账号验证通过,返回密码供SDK校验\n", __func__);
        } else {
            printf("[%s] 错误:服务器未配置密码,拒绝连接\n", __func__);
        }
    } else {
        // 账号不匹配:不填充密码,SDK会自动拒绝客户端连接
        printf("[%s] 未知账号 %s,拒绝连接\n", __func__, cszAccount);
    }
}
说明:生产环境中,账号密码建议存储在加密配置文件或云端,避免硬编码;同时需关闭明文日志打印。

回调2、会话信息扩展回调

接收客户端连接的详细信息(如连接模式、NAT类型、SessionID等),便于监控和问题排查。
/**
 * P2P隧道会话信息扩展回调函数
 * @brief 接收并处理会话详细信息(连接模式、NAT类型、版本等)
 * @param sSessionInfo 会话信息结构体指针(包含连接核心参数)
 * @param pArg 自定义参数(启动函数传入的args,当前未使用)
 */
void TunnelSessionInfoExCB(sP2PTunnelSessionInfoEx *sSessionInfo, const void *pArg) {
    // 校验输入参数有效性(避免空指针异常)
    if (sSessionInfo == NULL) {
        printf("[%s] 警告:会话信息结构体为空\n", __func__);
        return;
    }

    // 打印会话详细信息(用于监控和排查问题)
    printf("\n[%s] 会话信息更新:\n", __func__);
    
    // 解析连接模式(核心参数,影响传输速度)
    const char* modeStr = "";
    switch (sSessionInfo->nMode) {
        case 0: modeStr = "直连模式(最优,P2P直接通信)"; break;
        case 1: modeStr = "中继模式(通过TUTK服务器转发,速度较慢)"; break;
        case 2: modeStr = "局域网模式(同一局域网内通信,速度最快)"; break;
        default: modeStr = "未知模式(建议检查网络环境)";
    }
    printf("  连接模式:%d (%s)\n", sSessionInfo->nMode, modeStr);

    // 解析NAT类型(辅助排查穿透失败问题)
    printf("  NAT类型:%d(参考SDK文档NAT类型说明)\n", sSessionInfo->nNatType);

    // 版本与会话ID(用于版本兼容性校验和会话管理)
    printf("  P2PTunnelAPIs版本:0x%X\n", (unsigned int)sSessionInfo->nVersion);
    printf("  会话ID(SID):%d(用于后续IOTCAPIs接口调用)\n", sSessionInfo->nSID);

    // 远程客户端地址信息(仅中继/局域网模式有效)
    printf("  远程设备地址:%s:%d\n", 
           sSessionInfo->szRemoteIP ? sSessionInfo->szRemoteIP : "未知IP", 
           sSessionInfo->nRemotePort);
}
说明:nSID 为会话唯一标识,后续若需调用 IOTCAPIs 接口,需传入该 SID。
3.5 数据传输(无需额外开发)

步骤1启动上层TCP/IP服务

P2PTunnel Server 启动后,直接启动上层 TCP/IP 服务(如 SSH、HTTP、FTP 等),无需修改服务代码。隧道会自动转发对应端口的数据包。
  • SSH服务:启动 openssh-server(默认端口22);
  • HTTP服务:启动 Nginx/Apache(默认端口80);
  • RTSP服务:启动流媒体服务器(默认端口554);
  • 其他私有TCP服务:按原有逻辑启动,确保端口与隧道配置一致。
说明:数据传输过程由 SDK 自动处理,上层服务无需感知隧道存在,实现透明穿透。
3.6 Server 反初始化(必选)

步骤1停止隧道服务并释放资源

程序退出或需重启隧道服务时,调用反初始化接口释放资源,避免内存泄漏。
// 停止P2PTunnel Server(断开所有客户端连接)
P2PTunnelServer_Stop();

// 释放隧道服务器资源(反初始化)
P2PTunnelServerDeInitialize();
说明:反初始化接口需在程序退出前调用;若需重启服务,需先反初始化再重新执行初始化流程。
3.7 设备登录状态检测(推荐)

步骤1定期监控IOTC登录状态

设备通过心跳维持与 TUTK 服务器的连接,需定期检测登录状态,处理失联情况(如重启P2P模块)。
/**
 * 监控IOTC登录状态并处理异常
 * @brief 定期检查登录状态,当重试失败次数过多时执行恢复操作
 */
// 全局变量:控制进程运行状态
bool gProcessRun = true;

// 检查是否有用户正在访问(自定义实现,如统计当前连接数)
bool checkNoUserAccess() {
    // 示例逻辑:获取当前隧道连接数,0表示无用户访问
    int currentConn = P2PTunnelServer_Get_Connection_Count();  // 假设该接口获取连接数
    return (currentConn == 0);
}

// 检查网络是否畅通(自定义实现,如ping网关或TUTK服务器)
bool checkNetworkAvailability() {
    // 示例逻辑:ping TUTK服务器地址,成功返回true
    return system("ping -c 1 tutk-server-address > /dev/null 2>&1") == 0;
}

void monitorLoginState() {
    unsigned int login_state;  // 登录状态码(7表示正常登录并收到服务器响应)

    while (gProcessRun) {
        // 获取登录信息:返回值为重试登录失败次数,输出参数为当前登录状态
        int ret = IOTC_Get_Login_Info(&login_state);
        printf("IOTC_Get_Login_Info() - 重试失败次数: %d, 登录状态: %u\n", ret, login_state);

        // 处理接口调用错误(如SDK未初始化)
        if (ret < IOTC_ER_NoERROR) {
            printf("IOTC_Get_Login_Info() 调用失败,错误码: %d,退出监控循环\n", ret);
            break;
        }

        // 解析登录状态
        if (login_state == 7) {
            printf("登录状态正常:设备已成功注册到P2P服务器\n");
        } else {
            printf("登录状态异常:状态码=%u(非7表示未正常注册,需检查网络/密钥)\n", login_state);
        }

        // 处理长时间失联情况(ret>4表示连续5次重试失败,与服务器失联)
        if (ret > 4) {
            printf("警告:已连续%d次登录重试失败,设备与P2P服务器失联\n", ret);
            
            // 检查是否满足恢复条件:无用户访问 + 网络畅通
            bool noUserAccess = checkNoUserAccess();
            bool networkAvailable = checkNetworkAvailability();
            
            if (noUserAccess && networkAvailable) {
                printf("满足恢复条件(无用户访问且网络正常),执行P2P模块重启\n");
                
                // 1. 停止隧道服务
                P2PTunnelServer_Stop();
                // 2. 反初始化隧道
                P2PTunnelServerDeInitialize();
                // 3. 重新初始化并启动(复用之前的初始化流程)
                reInitAndStartTunnelServer();  // 自定义重启函数
                // 4. 其他清理操作(如重置连接计数)
            } else {
                printf("不满足恢复条件 - 有用户访问: %s, 网络畅通: %s\n",
                       noUserAccess ? "否" : "是",
                       networkAvailable ? "是" : "否");
            }
        }

        // 休眠5秒后再次检测(分5次1秒休眠,提升退出响应速度)
        for (int sleep_count = 0; sleep_count < 5 && gProcessRun; sleep_count++) {
            sleep(1);
        }
    }
}
说明:login_state=7 是 SDK 定义的正常登录状态,其他状态码含义参考 SDK 官方文档。

四、FAQ(常见问题)

P2PTunnelAPIs 可以使用 IOTCAPIs 的 API 吗?
可以,调用 IOTCAPIs 接口时,需传入 P2PTunnel 会话的 SID(从 TunnelSessionInfoExCB 回调中获取),即可通过隧道进行数据交互。
P2PTunnelAPIs 可以与其他模块(AV、RDT)一起使用吗?
可以,但集成复杂度较高,容易出现资源冲突或兼容性问题,通常不建议混合使用。若需同时实现音视频传输和隧道功能,建议优先使用 P2PTunnel 承载音视频 TCP 流。

五、相关资源

以下为 TUTK P2P SDK 相关开发文档,便于扩展学习和实操:

即刻开启您的物联网之旅

联系解决方案专家
Kalay App
资讯安全白皮书
全球专利布局
解决方案
新闻动态
公司动态
行业资讯
媒体报道
永续发展
经营者的话
社会参与
环境永续
公司治理

+86 755 27702549

7×24小时服务热线

法律声明 隐私权条款

关注“TUTK”

TUTK服务尽在掌握

© 2022 物联智慧科技(深圳)有限公司版权所有粤ICP备14023641号
在线咨询
扫一扫

TUTK服务尽在掌握

全国免费服务热线
+86 755 27702549

返回顶部