Libupnp test_init.c
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的主要功能是:
- 检查 UPnP 版本 Check library version (and formats)
- 检查可选功能 Check library optional features
- 初始化 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
打印调试信息,包含IfName
和DestPort
的值,帮助开发者跟踪函数的执行情况。
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默认关闭。