【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

If you are experiencing problems with the Universal Plug and Play service, your computer might not be able to automatically detect the presence of other networked devices, such as PCs, printers, Internet access points and so on. That is where the UPnP Test application comes in. This simple program is designed to help you identify the issues that prevent the UPnP protocol from functioning correctly. Before you get your hopes up, you should know that this tool does not solve the detected problems, but only performs a series of tests to identify the possible causes. One advantage is that the application does not require installation, so your system registry is not affected in any way. The interface is compact and simple, comprising only two panels: one that displays the test type and its short description and the other for viewing which of the tests passed and which failed. The program can verify whether the operating system provides support for the UPnP service and allows you to check if the Simple Service Discovery Protocol (SSDP) and the UPnPHost services are running. It also verifies the connection between your network adapter and your router and the system's capacity to receive UPnP messages, as well as the router's capability to report an external IP address. One of the tests is designed to check if the Windows firewall service is blocking the traffic between your router and the system, thus preventing UPnP from working. The results can be copied to your clipboard by simply pressing a button and the tests can be redone easily. If you want to fix the detected issues, the link in the main interface can prove useful. In conclusion, UPnP Test is a simple tool for detecting problems related to device-to-device networking. However, it can only suggest possible reasons why the UPnP is not working, fixing the detected issues is totally up to you.
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值