简体中文

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

星空无限传媒xk8027-高清全集免费看


导语


TUTK提供的P2PTunnel服务,类似于vpn服务。P2PTunnel服务启动后,将通过TUTK的私有协议,将上层传入的数据转发到对端,而且不需要知道对端的IP。
P2PTunnel模块,可以内嵌至厂商的程序内,也可以独立做成一个模块。对于一些基于TCP/IP的标准或者私有服务,比如http、ssh、ftp、telnet、rtsp,只需要简单几行代码,就可以完成接入。
P2PTunnel模块工作示意图
基于P2PTunnelAPIs和TCPIP协议开发NAS-device-1.png


P2PTunnelServer使用流程图

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


例子代码分析

Server的初始化:

// 设置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;
}

// 初始化 P2P 隧道服务器(根据 SDK 版本选择接口)
#if _USE_SDK_VERSION_BELOW_4_3_5_0_
// 旧版本初始化接口(SDK 版本 < 4.3.5.0)
// 参数:允许的最大访客连接数
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=开启局域网直连(提升速度,但局域网内不加密)
// 注意:客户端需同样设置为1才能启用直连模式
ret = P2PTunnelServerInitialize2(MAX_CONNECTION, 1);
if (ret != TUNNEL_ER_NoERROR) {
    // 修正:打印正确的函数名
    printf("P2PTunnelServerInitialize2() error[%d]!\n", ret);
    return -1;
}
#endif

// 注册连接状态回调函数
// 功能:当访客连接状态变更(如退出访问)时,通过 TunnelStatusCB 回调通知
P2PTunnelServer_GetStatus(TunnelStatusCB, (void *)args);

Server的初始化,只需要在程序开始调用一次。


端口白名单回调注册[可选]:

/**
 * P2P隧道端口验证回调函数
 * @brief 控制哪些端口允许通过隧道进行访问,实现端口级别的访问控制
 * @param nServicePort 待验证的服务端口号
 * @param pArg 自定义参数(当前未使用,传NULL)
 * @return 0=允许访问该端口,-1=拒绝访问该端口
 */
#define WEB_SERVICE_PORT    80    // Web服务端口
#define SSH_SERVICE_PORT    22    // SSH服务端口
#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;
    }
}

// 注册端口验证回调函数,实现端口访问控制
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");
}


启动Tunnel Server:

// 启动P2P隧道服务器(带扩展参数)
// 参数说明:
// - gUid:服务器唯一标识(设备UID)
// - TunnelServerAuthentication:客户端认证回调函数(验证连接合法性)
// - TunnelSessionInfoExCB:会话信息回调函数(获取/处理会话详情)
// - args:自定义参数(传递给回调函数)
int ret = P2PTunnelServer_Start_Ex(gUid, TunnelServerAuthentication, TunnelSessionInfoExCB, args);

// 处理启动结果
if (ret != TUNNEL_ER_NoERROR) {
    // 启动失败:输出错误码便于排查
    printf("P2PTunnelServer_Start_Ex() 启动失败,错误码: %d\n", ret);
    return -1;
} else {
    // 启动成功:提示服务器可通过互联网被访问
    printf("P2PTunnelServer_Start_Ex() 启动成功,设备UID: %s,可通过互联网接收连接\n", gUid);
}

启动server的参数里面,一个是验证账密的回调,一个是连接状态信息的回调。
账密验证验证:

/**
 * P2P隧道服务器端认证回调函数
 * @brief 验证客户端账号合法性,并返回对应密码供SDK验证
 * @param cszAccount 客户端提供的账号
 * @param cszPassword 输出参数:用于填充账号对应密码的缓冲区
 * @param nPasswordMaxLength cszPassword缓冲区的最大长度(含终止符)
 * @param pArg 自定义参数(由启动函数传入)
 */
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) {
        // 账号匹配:安全填充密码(防止缓冲区溢出)
        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);
    }
}


连接状态的回调:

/**
 * P2P隧道会话信息扩展回调函数
 * @brief 接收并处理会话详细信息(连接模式、NAT类型、版本等)
 * @param sSessionInfo 会话信息结构体指针,包含连接的详细参数
 * @param pArg 自定义参数(由启动函数传入,当前未使用)
 */
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 = "直连模式(最优)"; break;
        case 1: modeStr = "中继模式(通过服务器转发)"; break;
        case 2: modeStr = "局域网模式"; break;
        default: modeStr = "未知模式";
    }
    printf("  连接模式:%d (%s)\n", sSessionInfo->nMode, modeStr);

    // NAT类型说明(常见NAT类型参考)
    printf("  NAT类型:%d\n", sSessionInfo->nNatType);

    // 版本与会话ID
    printf("  P2PTunnelAPIs版本:0x%X\n", (unsigned int)sSessionInfo->nVersion);
    printf("  SessionID(SID):%d\n", sSessionInfo->nSID);

    // 远程地址信息
    printf("  远程设备地址:%s:%d\n", 
           sSessionInfo->szRemoteIP ? sSessionInfo->szRemoteIP : "未知IP", 
           sSessionInfo->nRemotePort);
}

数据传输:
Tunnel Server启动完成后,就可以开启其他服务了,比如说http-server, ftp-server,rtsp-server,ssh-server等等。此部分因是标准服务,不再赘述。


Server的反初始化:

P2PTunnelServer_Stop();P2PTunnelServerDeInitialize();

Server的反始化,通常只需要在程序结束时调用一次,中途如果需要重启服务,也需调用。


设备的登录状态检测:

正常情况下,设备向P2P服务器报到成功后,会维护与P2P服务器之间的心跳。但是不排除某些情况下,可能会从服务器掉线,所以可以通过IOTC_Get_LoginInfo持续检测当前的login状态。

/**
 * 监控IOTC登录状态并处理异常
 * @brief 定期检查登录状态,当重试失败次数过多且满足条件时执行登出操作
 */
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);

    // 处理接口调用错误
    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表示长时间与服务器失联)
    if (ret > 4) {
        printf("警告:已连续%d次登录重试失败,与P2P服务器失联\n", ret);
        
        // 检查是否满足登出条件:无用户访问且网络畅通
        bool noUserAccess = checkNoUserAccess();  // 替换为实际的"无用户访问"检查函数
        bool networkAvailable = checkNetworkAvailability();  // 替换为实际的网络畅通检查函数(如ping)
        
        if (noUserAccess && networkAvailable) {
            printf("满足登出条件(无用户访问且网络正常),执行P2P服务器登出\n");
            //  重启P2P模块
            // 其他清理操作...
            break;  // 退出监控循环
        } else {
            printf("不满足登出条件 - 有用户访问: %s, 网络畅通: %s\n",
                   noUserAccess ? "否" : "是",
                   networkAvailable ? "是" : "否");
        }
    }

    // 休眠5秒(每次休眠1秒并检查是否需要退出,提升响应速度)
    for (int sleep_count = 0; sleep_count < 5 && gProcessRun; sleep_count++) {
        sleep(1);
    }
}

FAQ:

1、P2PTunnelAPIs可以使用IOTCAPIs的API吗?
可以,参数为SID。

2、P2PTunnelAPIs可以与其他模块(AV、RDT)一起使用吗?
可以,但是会比较复杂,通常不建议一起用。

其他文章:

概论:引导
API比较:API比较
局域网设备搜索:基于IOTCAPIs的局域网设备搜索
实验1: [实验]如何基于P2PTunnelAPIs+ssh搭建一个轻NAS?
实验2:[实验]如何基于P2PTunnelAPIs+http搭建一个流媒体服务器

即刻开启您的物联网之旅

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

+86 755 27702549

7×24小时服务热线

法律声明 隐私权条款

关注“TUTK”

TUTK服务尽在掌握

© 2022 星空无限传媒xk8027版权所有粤ICP备14023641号
在线咨询
扫一扫

TUTK服务尽在掌握

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

返回顶部