自学内容网 自学内容网

【p2p、分布式,区块链笔记 UPNP】: Libupnp test_init.c 01 初始化SDK

Make

test/CMakeLists.txt

# https://github1s.com/pupnp/pupnp/blob/branch-1.14.x/upnp/test/CMakeLists.txt#L1-L4
UPNP_addUnitTest (test-upnp-init test_init.c)
UPNP_addUnitTest (test-upnp-list test_list.c)
UPNP_addUnitTest (test-upnp-log test_log.c)
UPNP_addUnitTest (test-upnp-url test_url.c)

UPNP_addUnitTest

  • 函数 UPNP_addUnitTest,用于add_test添加单元测试
    • 参数:testName (测试名称),sourceFile (测试的源文件)
// https://github1s.com/pupnp/pupnp/blob/branch-1.14.x/cmake/test-functions.cmake#L90-L112
function (UPNP_addUnitTest testName sourceFile)
    
    # 调用自定义函数 UPNP_addTestExecutable 创建测试可执行文件,传入 testName 和 sourceFile,
    # 使用add_executable 生成测试的可执行文件, target_link_libraries为可执行文件链接 upnp 库。
    UPNP_addTestExecutable (${testName} ${sourceFile})

    # 检查是否启用了共享库构建(UPNP_BUILD_SHARED 变量是否为真)
    if (UPNP_BUILD_SHARED)
        add_test (NAME ${testName}  # CTest函数 : 如果启用了共享库构建,则为该测试添加一个普通测试(动态库版本的测试)
            COMMAND ${testName}  # 测试命令为生成的可执行文件 testName
        )
        if (MSVC OR MSYS OR MINGW OR CYGWIN) # 如果使用的是 MSVC、MSYS、MinGW 或 Cygwin 环境(这些是 Windows 平台上的编译工具)
            UPNP_findTestEnv (${testName} TEST_ENV) # 调用自定义函数 UPNP_findTestEnv 来查找该测试的环境变量, 结果存储在 TEST_ENV 变量中
            set_tests_properties (${testName} PROPERTIES # CTest函数 : 设置该测试的属性,特别是设置环境变量(TEST_ENV),# 这些环境变量将在测试运行时生效
                ENVIRONMENT "${TEST_ENV}"  # 设置环境变量
            )
        endif()  # 结束平台检查的条件语句
    endif()  

    # 检查是否启用了静态库构建(UPNP_BUILD_STATIC 变量是否为真)
    if (UPNP_BUILD_STATIC)
        # 如果启用了静态库构建,则为该测试添加一个静态库版本的测试
        # 这里测试命令为 testName-static,即静态库版本的可执行文件
        add_test (NAME ${testName}-static
            COMMAND ${testName}-static  # 静态库版本的测试命令
        )
    endif()  
endfunction()  # 函数定义结束

CODE

test_init.c

  • 这是一个用于初始化并测试 UPnP(Universal Plug and Play) 库的 C 程序。UPnP 是一种支持设备自动化发现和与网络服务交互的协议,常用于智能设备、网络媒体服务器等。

  • test_init.c的主要功能是:

    1. 检查 UPnP 版本 Check library version (and formats)
    2. 检查可选功能 Check library optional features
    3. 初始化 UPnP 库 Test library initialisation,并输出服务器的 IP 地址和端口。
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
/**************************************************************************
 *
 * Copyright (c) 2006 Rémi Turboult <r3mi@users.sourceforge.net>
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * * Redistributions of source code must retain the above copyright notice,
 * this list of conditions and the following disclaimer.
 * * Redistributions in binary form must reproduce the above copyright notice,
 * this list of conditions and the following disclaimer in the documentation
 * and/or other materials provided with the distribution.
 * * Neither name of Intel Corporation nor the names of its contributors
 * may be used to endorse or promote products derived from this software
 * without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL INTEL OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 *************************************************************************/

#include "upnp.h"

#include <stdio.h>
#include <stdlib.h>

#if UPNP_HAVE_TOOLS // 根据条件编译 (#if UPNP_HAVE_TOOLS) 导入一些工具函数
#include "upnptools.h"
#endif

#include "upnpdebug.h"
#include "posix_overwrites.h"

int main(int argc, char *argv[])
{
int rc;
int a, b, c;
(void)argc;
(void)argv;
const char *log_file_name = "test_init.log";

/*
 * 1. 检查 UPnP 版本 Check library version (and formats)
 */
printf("\n");
printf("UPNP_VERSION_STRING = \"%s\"\n", UPNP_VERSION_STRING);
printf("UPNP_VERSION_MAJOR  = %d\n", UPNP_VERSION_MAJOR);
printf("UPNP_VERSION_MINOR  = %d\n", UPNP_VERSION_MINOR);
printf("UPNP_VERSION_PATCH  = %d\n", UPNP_VERSION_PATCH);
printf("UPNP_VERSION        = %d\n", UPNP_VERSION);

#ifdef _WIN32 // 如果是Windows平台
if (sscanf_s(UPNP_VERSION_STRING, "%d.%d.%d", &a, &b, &c) != 3 ||
#else
if (sscanf(UPNP_VERSION_STRING, "%d.%d.%d", &a, &b, &c) != 3 ||
#endif
a != UPNP_VERSION_MAJOR || b != UPNP_VERSION_MINOR ||
c != UPNP_VERSION_PATCH) {
printf("** ERROR malformed UPNP_VERSION_STRING\n");
exit(EXIT_FAILURE);
}

/*
 * 2. 检查可选功能 Check library optional features
 */
printf("\n");

#if UPNP_HAVE_DEBUG
printf("UPNP_HAVE_DEBUG \t= yes\n");
#else
printf("UPNP_HAVE_DEBUG \t= no\n");
#endif

#if UPNP_HAVE_CLIENT
printf("UPNP_HAVE_CLIENT\t= yes\n");
#else
printf("UPNP_HAVE_CLIENT\t= no\n");
#endif

#if UPNP_HAVE_DEVICE
printf("UPNP_HAVE_DEVICE\t= yes\n");
#else
printf("UPNP_HAVE_DEVICE\t= no\n");
#endif

#if UPNP_HAVE_WEBSERVER
printf("UPNP_HAVE_WEBSERVER\t= yes\n");
#else
printf("UPNP_HAVE_WEBSERVER\t= no\n");
#endif

#if UPNP_HAVE_TOOLS
printf("UPNP_HAVE_TOOLS \t= yes\n");
#else
printf("UPNP_HAVE_TOOLS \t= no\n");
#endif

/*
 * 3. 初始化 UPnP 库 Test library initialisation
 */
printf("\n");
printf("Initializing UPnP ... \n");
unlink(log_file_name);
UpnpSetLogFileNames(log_file_name, 0);
rc = UpnpInit2(NULL, 0); // 调用 UpnpInit2() 函数来初始化 UPnP 库,并指定日志文件。
if (UPNP_E_SUCCESS == rc) {
const char *ip_address = UpnpGetServerIpAddress();
unsigned short port = UpnpGetServerPort();

printf("UPnP Initialized OK ip=%s, port=%d\n",
(ip_address ? ip_address : "UNKNOWN"),
port);
} else {
printf("** ERROR UpnpInit2(): %d", rc);
#if UPNP_HAVE_TOOLS
printf(" %s", UpnpGetErrorMessage(rc));
#endif
printf("\n");
exit(EXIT_FAILURE);
}

(void)UpnpFinish(); // 调用 UpnpFinish() 清理库的资源。
printf("\n");

exit(EXIT_SUCCESS);
}

UpnpInit2(NULL, 0)函数来初始化 UPnP 库:

  • UpnpInit2函数负责初始化 UPnP(Universal Plug and Play) SDK。它接收两个参数,一个是网络接口名称 IfName,另一个是目标端口 DestPort,并返回初始化的结果状态码。
int UpnpInit2(const char *IfName, unsigned short DestPort)
{
int retVal;

/* Initializes the ithread library */
ithread_initialize_library();

ithread_mutex_lock(&gSDKInitMutex);// 使用 gSDKInitMutex 互斥锁来保护对共享资源的访问,防止多个线程同时执行初始化过程,确保线程安全。

/* Check if we're already initialized. */
if (UpnpSdkInit == 1) {
retVal = UPNP_E_INIT;
goto exit_function;
}

/* Set the UpnpSdkInit flag to 1 to indicate we're successfully
 * initialized. */
UpnpSdkInit = 1;

/* Perform initialization preamble. */
retVal = UpnpInitPreamble();
if (retVal != UPNP_E_SUCCESS) {
goto exit_function;
}

UpnpPrintf(UPNP_INFO,
API,
__FILE__,
__LINE__,
"UpnpInit2 with IfName=%s, DestPort=%d.\n",
IfName ? IfName : "NULL",
DestPort);

/* Retrieve interface information (Addresses, index, etc). */
retVal = UpnpGetIfInfo(IfName);
if (retVal != UPNP_E_SUCCESS) {
goto exit_function;
}

/* Finish initializing the SDK. */
retVal = UpnpInitStartServers(DestPort);
if (retVal != UPNP_E_SUCCESS) {
goto exit_function;
}

exit_function:
if (retVal != UPNP_E_SUCCESS && retVal != UPNP_E_INIT) {
UpnpFinish();
}
ithread_mutex_unlock(&gSDKInitMutex);

return retVal;
}

  • UpnpInit2 函数负责(其调用了多个函数,下列不重要的部分将用*标记):
    • 初始化 UPnP SDK 的线程库和网络接口。
    • 检查是否已经初始化,防止重复初始化。
    • 通过互斥锁确保线程安全。
    • 获取网络接口信息并启动 SDK 的核心服务。
    • 如果过程中发生错误,进行资源清理并返回错误码。

1.函数 ithread_initialize_library*

  • ithread_initialize_library 是一个静态、内联函数,用于初始化库的线程相关内容。
  • ithread_initialize_library 函数目前仅返回 0,表示成功。这个函数的主要目的可能是为库初始化线程相关功能预留一个接口,虽然当前实现为空函数。
/****************************************************************************
 * Function: ithread_initialize_library
 *
 *  Description:
 *      Initializes the library. Does nothing in all implementations, except
 *      when statically linked for WIN32.
 *  Parameters:
 *      none.
 *  Returns:
 *      0 on success, Nonzero on failure.
 ***************************************************************************/
static UPNP_INLINE int ithread_initialize_library(void)
{
int ret = 0;

return ret;
}
相关的宏定义
  • UPNP_INLINE:此宏代表内联(inline)函数修饰符。根据平台的不同(如微软的 Visual C++ 编译器),UPNP_INLINE 宏可能会展开为 inline_inline
  • 微软的 Visual C++ 编译器 (_MSC_VER) 小于等于 1900 的版本使用 _inline,而更高版本使用标准的 inline
#ifdef UPNP_USE_MSVCPP
    #if _MSC_VER > 1900
        #define UPNP_INLINE inline
        #define PRIzd "zd"
        #define PRIzu "zu"
        #define PRIzx "zx"
    #else
        #define UPNP_INLINE _inline
        typedef __int64 int64_t;
        #define PRIzd "ld"
        #define PRIzu "lu"
        #define PRIzx "lx"
    #endif
#endif /* UPNP_USE_MSVCPP */
  • PRIzd, PRIzu, PRIzx:格式说明符,用于打印不同大小的数据类型。它们在不同平台上可能有所不同:
    • PRIzd:用于有符号整数的打印。
    • PRIzu:用于无符号整数的打印。
    • PRIzx:用于以十六进制形式打印无符号整数。

2.互斥锁加锁*

// 此函数是 POSIX 线程库中的 pthread_mutex_lock 函数封装 #define ithread_mutex_lock pthread_mutex_lock https://github1s.com/pupnp/pupnp/blob/branch-1.14.x/upnp/inc/ithread.h#L352-L367
ithread_mutex_lock(&gSDKInitMutex);// gSDKInitMutex是pthread_mutex_t的锁对象。
// 后边会释放锁: ithread_mutex_unlock(&gSDKInitMutex);
  • 使用 gSDKInitMutex 互斥锁来保护对共享资源的访问,防止多个线程同时执行初始化过程,确保线程安全。

3.检查是否已初始化*

if (UpnpSdkInit == 1) {
    retVal = UPNP_E_INIT;
    goto exit_function; // 跳转到 `exit_function` 清理资源
}
  • 通过检查全局变量 UpnpSdkInit 来确定 SDK 是否已经初始化。如果 UpnpSdkInit == 1 表示 SDK 已经初始化过了,直接设置返回值为 UPNP_E_INIT 并跳转到 exit_function,避免重复初始化。

4.标记 SDK 已初始化*

UpnpSdkInit = 1; // 将 UpnpSdkInit 设置为 1,表示 SDK 已经成功初始化。

5.执行初始化前的步骤(重要)

retVal = UpnpInitPreamble();
if (retVal != UPNP_E_SUCCESS) {
    goto exit_function;
}
  • 调用 UpnpInitPreamble() 以执行一些初始化的预处理步骤。如果该步骤失败(返回非 UPNP_E_SUCCESS),则直接跳转到 exit_function 清理资源。
  • 此函数定义在https://github1s.com/pupnp/pupnp/blob/branch-1.14.x/upnp/src/api/upnpapi.c#L393-L479,详细解释连接

6.打印调试信息*

UpnpPrintf(UPNP_INFO,
    API,
    __FILE__,
    __LINE__,
    "UpnpInit2 with IfName=%s, DestPort=%d.\n",
    IfName ? IfName : "NULL",
    DestPort);
  • 通过 UpnpPrintf 打印调试信息,包含 IfNameDestPort 的值,帮助开发者跟踪函数的执行情况。

7.获取网络接口信息(重要)

retVal = UpnpGetIfInfo(IfName);
if (retVal != UPNP_E_SUCCESS) {
    goto exit_function;
}
  • 调用 UpnpGetIfInfo() 来检索网络接口的相关信息(如 IP 地址、接口索引等)。如果获取接口信息失败,直接跳转到 exit_function 进行清理。

8.启动 SDK 服务(重要)

retVal = UpnpInitStartServers(DestPort);
if (retVal != UPNP_E_SUCCESS) {
    goto exit_function;
}
  • 调用 UpnpInitStartServers() 启动 UPnP SDK 的必要服务,如设备发现服务、控制点等。如果启动失败,同样跳转到 exit_function

9.退出函数并清理资源(包括释放互斥锁)*

exit_function:
if (retVal != UPNP_E_SUCCESS && retVal != UPNP_E_INIT) {
    UpnpFinish();
}
ithread_mutex_unlock(&gSDKInitMutex);
  • exit_function 标签是一个统一的清理出口。如果初始化失败并且返回值既不是 UPNP_E_SUCCESS 也不是 UPNP_E_INIT,则调用 UpnpFinish() 清理已经分配的资源。
  • 最后,无论初始化是否成功,都会释放互斥锁 gSDKInitMutex,允许其他线程继续操作。

10. 返回初始化结果*

return retVal;
  • 返回初始化结果。如果一切成功,返回 UPNP_E_SUCCESS;否则返回相应的错误代码。其中UPNP_E_SUCCESS定义为0。
/*!
* \brief The operation completed successfully.
*
* For asynchronous functions, this only means that the packet generated by
* the operation was successfully transmitted on the network.  The result of
* the entire operation comes as part of the callback for that operation.
*/
#define UPNP_E_SUCCESS 0

运行结果

在这里插入图片描述

  • DEBUG为no是因为编译时没有设置:--enable-debug,debug默认关闭。

CG


原文地址:https://blog.csdn.net/ResumeProject/article/details/142701954

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!