当前位置: 首页 > news >正文

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

启动前全局资源配置

      • 代码解析
      • 函数分析
      • 代码中的重要部分
      • 1. Winsock 初始化 (`WinsockInit`):
      • 2. 锁初始化资源 (`UpnpInitMutexes`):
      • 3. 句柄表HandleTable(SDK 内部资源的表)初始化:
      • 4.线程池初始化 (`UpnpInitThreadPools`):
      • 5. 回调函数设置:
        • 5.1. SOAP(Simple Object Access Protocol)简单对象访问协议
        • 5.2. GENA(General Event Notification Architecture)通用事件通知架构
      • 6.定时器线程初始化 (`TimerThreadInit`):

在这里插入图片描述

  • 该代码是用于初始化 UPnP(Universal Plug and Play)SDK 的初始步骤的实现。英文preamble /ˈpriːæmbl/ 意为n.序言;前言;导言;开场白;绪论 v. 作序言[绪论]。函数 UpnpInitPreamble() 负责执行一系列关键任务,以确保 UPnP SDK 在使用之前的各个组件都被正确配置和启动。

代码解析

/*! * \brief 初始化过程如下 Performs the initial steps in initializing the UPnP SDK.** \li **初始化 Winsock 库**(仅限 Windows)                Winsock library is initialized for the process (Windows specific). * \li **初始化日志系统**,用于调试信息输出。                The logging (for debug messages) is initialized.* \li **初始化互斥锁、句柄表和线程池**,确保多线程环境中的安全和稳定。 Mutexes, Handle table and thread pools are allocated and initialized.* \li **设置 SOAP 和 GENA 的回调函数**,如果启用了这些功能。 Callback functions for SOAP and GENA are set, if they're enabled.* \li **初始化 SDK 的定时器线程**,用于定时任务。           The SDK timer thread is initialized.** \return UPNP_E_SUCCESS on success.*/

函数分析

static int UpnpInitPreamble(void) {int retVal = UPNP_E_SUCCESS;  // 初始化返回值为成功int i;                        // 循环变量#ifdef UPNP_HAVE_OPTSSDP // FALSEuuid_upnp nls_uuid;           // 用于 SSDP 的 NLS UUID
#endif /* UPNP_HAVE_OPTSSDP */// WinsockInit根据平台初始化 Winsock 库(Windows 平台特有,Winsock 是 Windows 中用于网络编程的 API,定义在https://github1s.com/pupnp/pupnp/blob/branch-1.14.x/upnp/src/api/upnpapi.c#L281-L319)//  Linux 上不需要显式地初始化网络库,也不需要像 Windows 上那样调用 WSAStartup() 和 WSACleanup()retVal = WinsockInit();if (retVal != UPNP_E_SUCCESS) {return retVal;  // Winsock 初始化失败,返回错误代码}// 通过时间来初始化随机数种子,用于 SDK 内部的随机数生成srand((unsigned int)time(NULL));// 初始化日志系统,用于调试输出retVal = UpnpInitLog();if (retVal != UPNP_E_SUCCESS) {// 如果日志系统初始化失败,返回通用初始化失败错误return UPNP_E_INIT_FAILED;}UpnpPrintf(UPNP_INFO, API, __FILE__, __LINE__, "Inside UpnpInitPreamble\n");// 初始化 SDK 全局互斥锁retVal = UpnpInitMutexes();if (retVal != UPNP_E_SUCCESS) {return retVal;  // 互斥锁初始化失败,返回错误}#ifdef UPNP_HAVE_OPTSSDP// 如果启用了 SSDP,生成并设置 NLS UUIDuuid_create(&nls_uuid);upnp_uuid_unpack(&nls_uuid, gUpnpSdkNLSuuid);
#endif /* UPNP_HAVE_OPTSSDP */// 初始化句柄列表HandleLock();  // 加锁,防止多线程竞争for (i = 0; i < NUM_HANDLE; ++i) {HandleTable[i] = NULL;  // 将句柄表初始化为空}HandleUnlock();  // 解锁// 初始化 SDK 的全局线程池retVal = UpnpInitThreadPools();if (retVal != UPNP_E_SUCCESS) {return retVal;  // 线程池初始化失败,返回错误}#ifdef INCLUDE_DEVICE_APIS // FALSE#if EXCLUDE_SOAP == 0// 如果启用了 SOAP,设置 SOAP 的回调函数SetSoapCallback(soap_device_callback);#endif
#endif /* INCLUDE_DEVICE_APIS */#ifdef INTERNAL_WEB_SERVER#if EXCLUDE_GENA == 0// 如果启用了 GENA,设置 GENA 的回调函数SetGenaCallback(genaCallback);#endif
#endif /* INTERNAL_WEB_SERVER */// 初始化 SDK 定时器线程retVal = TimerThreadInit(&gTimerThread, &gSendThreadPool);if (retVal != UPNP_E_SUCCESS) {// 如果定时器线程初始化失败,调用 UpnpFinish 清理资源UpnpFinish();return retVal;}return UPNP_E_SUCCESS;  // 初始化成功
}

代码中的重要部分

1. Winsock 初始化 (WinsockInit):

  • 此函数用于初始化 Winsock 库(如果是Windows系统)。Winsock 是 Windows 中用于网络编程的 API。
  • WinsockInit根据平台初始化 Winsock 库(Windows 平台特有,Winsock 是 Windows 中用于网络编程的 API,定义在https://github1s.com/pupnp/pupnp/blob/branch-1.14.x/upnp/src/api/upnpapi.c#L281-L319)
  • Linux 上不需要显式地初始化网络库,也不需要像 Windows 上那样调用 WSAStartup() 和 WSACleanup()。

2. 锁初始化资源 (UpnpInitMutexes):

  • 互斥锁(mutex)用于在线程间保护共享资源。这个函数初始化 SDK 的全局互斥锁,以确保资源访问的同步性。(比如在后续的uuid创建时,需要调用如下代码,先加锁,操作,后解锁)
extern ithread_mutex_t gUUIDMutex;#define UUIDLock() ithread_mutex_lock(&gUUIDMutex)
#define UUIDUnlock() ithread_mutex_unlock(&gUUIDMutex)
  • 其中ithread_mutex_init、ithread_rwlock_init等函数同样是对POSIX线程库中的函数封装。
/*!* \brief 初始化 UPnP SDK 使用的全局互斥锁。** \return UPNP_E_SUCCESS: 初始化成功*         UPNP_E_INIT_FAILED: 互斥锁初始化失败*/
static int UpnpInitMutexes(void)
{
#ifdef __CYGWIN__// 在 Cygwin 系统上,pthread_mutex_init() 函数在某些情况下会失败。// 为了解决这个问题,需要在调用该函数之前将 GlobalHndRWLock 结构体清零。// 这是一个临时解决方案,未来应该修复 Cygwin 的这个 bug。memset(&GlobalHndRWLock, 0, sizeof(GlobalHndRWLock));
#endif// 初始化全局读写锁。// 读写锁允许多个线程同时读取共享数据,但同一时刻只能有一个线程写入。if (ithread_rwlock_init(&GlobalHndRWLock, NULL) != 0) {// 如果初始化失败,返回错误码。return UPNP_E_INIT_FAILED;}// 初始化全局 UUID 互斥锁。// 互斥锁确保同一时刻只有一个线程可以访问 UUID 生成相关的代码。if (ithread_mutex_init(&gUUIDMutex, NULL) != 0) {return UPNP_E_INIT_FAILED;}// 如果定义了 INCLUDE_CLIENT_APIS,则初始化全局订阅互斥锁。// 订阅互斥锁用于保护订阅相关的操作。
#ifdef INCLUDE_CLIENT_APISif (ithread_mutex_init(&GlobalClientSubscribeMutex, NULL) != 0) {return UPNP_E_INIT_FAILED;}
#endif// 如果所有互斥锁都初始化成功,则返回成功。return UPNP_E_SUCCESS;
}

3. 句柄表HandleTable(SDK 内部资源的表)初始化:

  • 句柄表是用于管理 SDK 内部资源的表。在多线程环境下,必须加锁保护:
/*! UPnP device and control point handle table  */ // https://github1s.com/pupnp/pupnp/blob/branch-1.14.x/upnp/src/api/upnpapi.c#L187-L188 
static void *HandleTable[NUM_HANDLE];
  • 这里首先通过 HandleLock() 函数加锁,然后初始化所有句柄为 NULL,最后通过 HandleUnlock() 解除锁定。
  • HandleLock()HandleUnlock() 定义如下:
/*!* \brief Get handle information.** \return HND_DEVICE, UPNP_E_INVALID_HANDLE*/
Upnp_Handle_Type GetHandleInfo(/*! handle pointer (key for the client handle structure). */int Hnd,/*! handle structure passed by this function. */struct Handle_Info **HndInfo);#define HandleLock() HandleWriteLock()#define HandleWriteLock() \UpnpPrintf( \UPNP_INFO, API, __FILE__, __LINE__, "Trying a write lock\n"); \ithread_rwlock_wrlock(&GlobalHndRWLock); \UpnpPrintf(UPNP_INFO, API, __FILE__, __LINE__, "Write lock acquired\n");#define HandleReadLock() \UpnpPrintf( \UPNP_INFO, API, __FILE__, __LINE__, "Trying a read lock\n"); \ithread_rwlock_rdlock(&GlobalHndRWLock); \UpnpPrintf(UPNP_INFO, API, __FILE__, __LINE__, "Read lock acquired\n");#define HandleUnlock() \UpnpPrintf(UPNP_INFO, API, __FILE__, __LINE__, "Trying Unlock\n"); \ithread_rwlock_unlock(&GlobalHndRWLock); \UpnpPrintf(UPNP_INFO, API, __FILE__, __LINE__, "Unlocked rwlock\n");

4.线程池初始化 (UpnpInitThreadPools):

  • 线程池用于处理 SDK 中的异步任务。UpnpInitThreadPools 函数确保线程池正确初始化,以便 SDK 能够高效处理任务。
/*!* \brief 初始化 UPnP SDK 所使用的全局线程池** \return 成功时返回 UPNP_E_SUCCESS,若互斥锁无法初始化则返回 UPNP_E_INIT_FAILED*/
static int UpnpInitThreadPools(void)
{// 定义返回值并初始化为成功标志int ret = UPNP_E_SUCCESS;// 定义线程池属性结构体ThreadPoolAttr attr;// 初始化线程池属性TPAttrInit(&attr);// 设置线程池的最大线程数TPAttrSetMaxThreads(&attr, MAX_THREADS);// 设置线程池的最小线程数TPAttrSetMinThreads(&attr, MIN_THREADS);// 设置线程栈的大小TPAttrSetStackSize(&attr, THREAD_STACK_SIZE);// 设置每个线程最多处理的任务数TPAttrSetJobsPerThread(&attr, JOBS_PER_THREAD);// 设置线程空闲的时间TPAttrSetIdleTime(&attr, THREAD_IDLE_TIME);// 设置线程池允许的最大任务总数TPAttrSetMaxJobsTotal(&attr, MAX_JOBS_TOTAL);// 初始化发送线程池,若失败则跳转到 exit_function 退出if (ThreadPoolInit(&gSendThreadPool, &attr) != UPNP_E_SUCCESS) {ret = UPNP_E_INIT_FAILED;goto exit_function;}// 初始化接收线程池,若失败则跳转到 exit_function 退出if (ThreadPoolInit(&gRecvThreadPool, &attr) != UPNP_E_SUCCESS) {ret = UPNP_E_INIT_FAILED;goto exit_function;}// 初始化迷你服务器线程池,若失败则跳转到 exit_function 退出if (ThreadPoolInit(&gMiniServerThreadPool, &attr) != UPNP_E_SUCCESS) {ret = UPNP_E_INIT_FAILED;goto exit_function;}exit_function:// 如果初始化失败,设置 SDK 初始化状态为 0,并调用 UpnpFinish 清理if (ret != UPNP_E_SUCCESS) {UpnpSdkInit = 0;UpnpFinish();}// 返回初始化结果return ret;
}
  • ThreadPoolInit函数的操作过程详见此链接🔗,这是项目中唯一调用并创建线程池的地方,后续的代码都直接使用此线程池。线程池结构体定义见连接,其仍使用mutex枷锁和条件变量唤醒线程,同时增加对线程队列进行优先级划分(低中高有限度和持久线程,持久线程为ThreadPoolJob类型,低中高为带有比较函数的LinkedList(区别标准库的std::list)类型)。
    在这里插入图片描述

5. 回调函数设置:

  • 如果启用了 SOAP 和 GENA 协议,分别设置它们的回调函数。这些函数用于处理设备 API 和事件通知。
5.1. SOAP(Simple Object Access Protocol)简单对象访问协议
  • SOAP 是一种基于 XML 的消息传输协议,设计用于在分布式网络环境中交换结构化信息。它常用于 web 服务中,尤其是在涉及复杂数据和远程过程调用 (RPC) 的场景下。
#ifdef INCLUDE_DEVICE_APIS#if EXCLUDE_SOAP == 0SetSoapCallback(soap_device_callback);#endif
#endif /* INCLUDE_DEVICE_APIS */
  • 函数SetSoapCallback(https://github1s.com/pupnp/pupnp/blob/branch-1.14.x/upnp/src/genlib/miniserver/miniserver.c#L139)定义如下:
#ifdef INCLUDE_DEVICE_APISvoid SetSoapCallback(MiniServerCallback callback) { gSoapCallback = callback; }
#endif /* INCLUDE_DEVICE_APIS */
  • 其传入的参数为定义为如下的函数指针类型:
/*! . */
typedef void (*MiniServerCallback)(/* ! [in] . 指向 http_parser_t 类型的指针,表示一个 HTTP 解析器,用于解析 HTTP 请求或响应 */http_parser_t *parser,/* ! [in] . 指向 http_message_t 类型的指针,表示一个 HTTP 请求消息,包含请求的详细内容。*/http_message_t *request,/* ! [in] .指向 SOCKINFO 类型的指针,表示套接字信息,可能包含有关网络连接的相关数据。 */SOCKINFO *info);
5.2. GENA(General Event Notification Architecture)通用事件通知架构
  • GENA 是一种用于异步事件通知的协议。它的主要目的是允许客户端订阅并接收来自服务器端的事件通知。GENA 协议最常见的使用是在 UPnP 环境中进行事件通知。
#ifdef INTERNAL_WEB_SERVER#if EXCLUDE_GENA == 0SetGenaCallback(genaCallback);#endif
#endif /* INTERNAL_WEB_SERVER */
  • 函数SetGenaCallback在https://github1s.com/pupnp/pupnp/blob/branch-1.14.x/upnp/src/genlib/miniserver/miniserver.c#L142
void SetGenaCallback(MiniServerCallback callback) { gGenaCallback = callback; }

6.定时器线程初始化 (TimerThreadInit):

  • 这是 SDK 的最后一个初始化步骤,用于启动一个专门处理定时任务的线程。如果这个步骤失败,会调用 UpnpFinish() 来清理已经分配的资源。
  • 函数TimerThreadInit的主要作用是初始化一个定时器线程,并将其添加到指定的线程池中,以便定时器能够在线程池中异步执行任务。
  • 通过 TPJobInit 初始化线程池任务,并设置任务的高优先级,然后将任务添加到线程池中,确保定时器线程能够持续运行。
int TimerThreadInit(TimerThread *timer, ThreadPool *tp)
{// 定义返回值并初始化为 0,表示无错误int rc = 0;// 定义线程池任务结构体,代表定时器线程的工作ThreadPoolJob timerThreadWorker;// 断言 timer 和 tp 不为空,如果为空则程序中断assert(timer != NULL);assert(tp != NULL);// 如果 timer 或者线程池 tp 为空,返回 EINVAL 错误码if ((timer == NULL) || (tp == NULL)) {return EINVAL;}// 初始化定时器互斥锁,rc 累加返回值(0 为成功)rc += ithread_mutex_init(&timer->mutex, NULL);// 断言互斥锁初始化成功assert(rc == 0);// 锁定定时器的互斥锁rc += ithread_mutex_lock(&timer->mutex);assert(rc == 0);// 初始化定时器的条件变量rc += ithread_cond_init(&timer->condition, NULL);assert(rc == 0);// 初始化定时器事件的空闲列表(FreeList),每个事件的大小为 TimerEvent,容量为 100rc += FreeListInit(&timer->freeEvents, sizeof(TimerEvent), 100);assert(rc == 0);// 将定时器的关闭标志设为 0(未关闭)timer->shutdown = 0;// 设置定时器的线程池为传入的 tptimer->tp = tp;// 初始化最后一个事件 ID 为 0timer->lastEventId = 0;// 初始化事件队列(eventQ),事件队列存储定时器的所有事件rc += ListInit(&timer->eventQ, NULL, NULL);assert(rc == 0);// 如果初始化过程有错误(rc != 0),则返回 EAGAIN 错误码if (rc != 0) {rc = EAGAIN;} else {// 初始化线程池任务,指定工作函数 TimerThreadWorker,并传入定时器结构体TPJobInit(&timerThreadWorker, TimerThreadWorker, timer);// 设置线程池任务的优先级为高优先级TPJobSetPriority(&timerThreadWorker, HIGH_PRIORITY);// 将任务添加到线程池,作为一个持久任务运行rc = ThreadPoolAddPersistent(tp, &timerThreadWorker, NULL);}// 解锁定时器的互斥锁ithread_mutex_unlock(&timer->mutex);// 如果任务添加失败,则销毁已经初始化的资源(条件变量、互斥锁、空闲列表、事件队列)if (rc != 0) {ithread_cond_destroy(&timer->condition);ithread_mutex_destroy(&timer->mutex);FreeListDestroy(&timer->freeEvents);ListDestroy(&timer->eventQ, 0);}// 返回初始化的结果码,0 表示成功,非 0 表示错误return rc;
}

http://www.mrgr.cn/news/47993.html

相关文章:

  • adb无法连接到安卓设备【解决方案】报错:adb server version (40) doesn‘t match this client (41);
  • 重启ubuntu服务器,如何让springboot服务自动运行
  • 基于springboot的海洋知识服务平台的设计与实现
  • 前端往后端传递参数的方式有哪些?
  • Kubernetes、Docker 和 Docker Registry 关系是是什么?
  • Junit如何禁用指定测试类,及使用场景
  • 如何创建一个node.js项目并配置
  • 【Lua学习】数值number和数学库math
  • MacOS 同时配置github、gitee和gitlab密钥
  • 信息安全数学基础(26)二次互反律
  • DevOps
  • 【Spring】@Autowired注解自动装配的过程
  • 2025届计算机保研经验贴(末九→浙江大学软件学院)
  • uniapp、微信小程序、Vue中使用nzh库实现数字转中文大写
  • 服务器部署‌Traefik 实现子级域名路由服务(对外子域名80,路由对内大端口)
  • STM32—SPI通信外设
  • 什么是毛利净利润
  • 【网络安全】CVE-2024-46990: Directus环回IP过滤器绕过实现SSRF
  • 【LeetCode】动态规划—646. 最长数对链(附完整Python/C++代码)
  • DIFY上使用多种大语言模型(MindCraft API)
  • 《Linux从小白到高手》综合应用篇:详解Linux系统调优之服务器硬件优化
  • BypassUAC
  • 深度学习之常用数据集下载
  • C# Json文件写入、读取 ,Json文件序列化、反序列化
  • 【Java 22 | 1】 深入解析Java 22 :增强的模式匹配特性
  • 2024下半年软考中级网络工程师,这100题,必做!