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

curl获取ip定位信息 --- libcurl-multi(三)

本文主要介绍使用libcurl multi进行curl ip_api.com的操作,ip_api.com和cjson解析,参考system专栏内容。

libcurlmulti 接口用于并发处理多个 HTTP 请求。它允许在单个线程中同时处理多个连接,而无需阻塞。这个多重请求接口(curl_multi_*)常用于需要发送多个 HTTP 请求的应用,如爬虫、API 聚合器等。

1. libcurl multi

1.1 基本概念

libcurlmulti 接口通过将多个 CURL 句柄组合在一个多重会话中来进行并发请求。每个句柄代表一个独立的请求,通过 curl_multi_* 函数进行管理和处理。其安装方式不做概述, libcurl - multi interface overview,该网站详细介绍了libcurl multi的api以及demo用例,可做参考。

在这里插入图片描述

1.2 常用api简介

简单介绍一下常用的api,具体的参考官网。

  • curl_multi_init():初始化一个多重会话。
CURLM *multi_handle = curl_multi_init();
  • curl_multi_add_handle():将一个 CURL 句柄添加到多重会话中。
CURL *handle = curl_easy_init();
curl_multi_add_handle(multi_handle, handle);
  • curl_multi_remove_handle():从多重会话中移除一个 CURL 句柄。
curl_multi_remove_handle(multi_handle, handle);
  • curl_multi_perform():执行并发请求。该函数会阻塞直到所有请求都完成,或者直到请求有任何进展。
int still_running;
curl_multi_perform(multi_handle, &still_running);
  • curl_multi_wait():使当前线程阻塞,直到有活动的请求需要处理。该函数通常配合 curl_multi_perform() 使用,以提高效率。
int numfds;
curl_multi_wait(multi_handle, NULL, 0, 1000, &numfds);
  • curl_multi_cleanup():清理多重会话,释放资源。
curl_multi_cleanup(multi_handle);

注意事项

  • curl_multi_perform() 在执行时可能并不会立即完成所有请求,因此你需要反复调用它,直到所有请求都完成。你还可以结合 curl_multi_wait() 来优化效率,避免持续轮询。
  • 确保在多线程环境中,所有 CURL 操作都由适当的线程管理,尤其是在处理大量并发请求时。

通过这种方式,libcurl 可以高效地并行处理多个 HTTP 请求。

1.3 简单demo

下述为一个简单的使用demo,更多的demo参考官网。

#include <stdio.h>
#include <stdlib.h>
#include <curl/curl.h>int main() {CURL *easy_handle1, *easy_handle2;CURLM *multi_handle;int still_running;// 初始化 multi 会话multi_handle = curl_multi_init();// 初始化两个 easy handleseasy_handle1 = curl_easy_init();easy_handle2 = curl_easy_init();// 设置 easy handle1 的 URLcurl_easy_setopt(easy_handle1, CURLOPT_URL, "http://example.com");// 设置 easy handle2 的 URLcurl_easy_setopt(easy_handle2, CURLOPT_URL, "http://example.org");// 添加两个句柄到 multi 会话curl_multi_add_handle(multi_handle, easy_handle1);curl_multi_add_handle(multi_handle, easy_handle2);// 执行请求,直到完成do {curl_multi_perform(multi_handle, &still_running);} while (still_running);// 清理curl_multi_remove_handle(multi_handle, easy_handle1);curl_multi_remove_handle(multi_handle, easy_handle2);curl_easy_cleanup(easy_handle1);curl_easy_cleanup(easy_handle2);curl_multi_cleanup(multi_handle);return 0;
}

2. 封装multi_curl接口

使用libcurl多线程方式获取IP相关信息的函数:

#include <stdio.h>
#include <string.h>
#include <curl/curl.h>typedef struct {char *memory;size_t size;
} MemoryStruct;void mulit_get_ip_info(const char *curl_url) {CURLM *multiHandle = NULL;CURL *run_curl = NULL;MemoryStruct chunk = {0};int still_running = 0;CURLcode res;// 初始化全局环境if (curl_global_init(CURL_GLOBAL_DEFAULT) != CURLE_OK) {fprintf(stderr, "Curl全局初始化失败。\n");return;}// 初始化multi句柄multiHandle = curl_multi_init();if (!multiHandle) {fprintf(stderr, "Curl multi初始化失败。\n");curl_global_cleanup();return;}// 为响应数据分配内存chunk.memory = malloc(1);chunk.size = 0;if (!chunk.memory) {fprintf(stderr, "内存分配失败。\n");curl_multi_cleanup(multiHandle);curl_global_cleanup();return;}// 初始化请求并设置选项run_curl = InitCurlRequest(curl_url, &chunk);if (!run_curl) {fprintf(stderr, "初始化curl请求失败。\n");free(chunk.memory);curl_multi_cleanup(multiHandle);curl_global_cleanup();return;}// 将curl句柄添加到multi句柄res = curl_multi_add_handle(multiHandle, run_curl);if (res != CURLM_OK) {fprintf(stderr, "将句柄添加到multi句柄失败: %s\n", curl_multi_strerror(res));free(chunk.memory);curl_easy_cleanup(run_curl);curl_multi_cleanup(multiHandle);curl_global_cleanup();return;}// 执行请求do {res = curl_multi_perform(multiHandle, &still_running);if (res != CURLM_OK) {fprintf(stderr, "curl multi执行错误: %s\n", curl_multi_strerror(res));break;}} while (still_running);/******************************* 添加解析函数 ******************************/// 解析响应(此部分应该实现)// 清理并释放资源curl_multi_remove_handle(multiHandle, run_curl);curl_easy_cleanup(run_curl);curl_multi_cleanup(multiHandle);curl_global_cleanup();// 释放分配的内存if(chunk.memory) {free(chunk.memory);chunk.memory = NULL;chunk.size = 0;}
}

3.完整示例

下列为添加了解析部分的完整示例
注意: 建议将该接口放置在线程中/task中进行调用,根据调度系统选择对应的方式,下文仅为示例代码

#include <stdio.h>
#include <string.h>
#include "cJSON.h"
#include <curl/curl.h>#define CURL_IP_API     "http://ip-api.com/json/?fields=status,message,countryCode,regionName,city,lat,lon"typedef struct ip_info {char countryCode[8];char regionName[32];char city[32];double lat;double lon;
} ip_info;typedef struct {char *memory;size_t size;
} MemoryStruct;ip_info current_ip_info = {0};int parse_ip_json_str(cJSON *current_json_str){int ret = -1;cJSON *status = cJSON_GetObjectItemCaseSensitive(current_json_str, "status");if (!cJSON_IsString(status) || strcmp(status->valuestring, "success") != 0) {ret = -1;goto end;}cJSON *item;if ((item = cJSON_GetObjectItem(current_json_str, "countryCode")) && cJSON_IsString(item)) strncpy(current_ip_info.countryCode, item->valuestring, sizeof(current_ip_info.countryCode)-1);if ((item = cJSON_GetObjectItem(current_json_str, "regionName")) && cJSON_IsString(item)) strncpy(current_ip_info.regionName, item->valuestring, sizeof(current_ip_info.regionName)-1);if ((item = cJSON_GetObjectItem(current_json_str, "city")) && cJSON_IsString(item)) strncpy(current_ip_info.city, item->valuestring, sizeof(current_ip_info.city)-1);if ((item = cJSON_GetObjectItem(current_json_str, "lat")) && cJSON_IsNumber(item)) current_ip_info.lat = item->valuedouble;if ((item = cJSON_GetObjectItem(current_json_str, "lon")) && cJSON_IsNumber(item)) current_ip_info.lon = item->valuedouble;if(strlen(current_ip_info.countryCode) != 0 && strlen(current_ip_info.regionName) != 0 && strlen(current_ip_info.city) != 0 && current_ip_info.lat != 0 && current_ip_info.lon != 0) ret = 0;
end:return ret;
}void print_ip_json_parse(void) {printf("current ip info:\n");printf("countryCode ID: %s\n", current_ip_info.countryCode);printf("regionName: %s\n", current_ip_info.regionName);printf("city: %s\n", current_ip_info.city);printf("lat: %.4f\n", current_ip_info.lat);printf("lon: %.4f\n", current_ip_info.lon);printf("\n");
}size_t WriteMemoryCallback(void *contents, size_t size, size_t nmemb, void *userp) {size_t realsize = size * nmemb;MemoryStruct *mem = (MemoryStruct *)userp;char *ptr = realloc(mem->memory, mem->size + realsize + 1);if(!ptr) return 0;mem->memory = ptr;memcpy(&(mem->memory[mem->size]), contents, realsize);mem->size += realsize;mem->memory[mem->size] = 0;return realsize;
}void mulit_get_ip_info(char *curl_url) {CURLM *multiHandle = NULL;CURL *run_curl = NULL;MemoryStruct chunk = {0};int still_running = 0;CURLcode res;// 初始化全局环境if (curl_global_init(CURL_GLOBAL_DEFAULT) != CURLE_OK) {fprintf(stderr, "Curl全局初始化失败。\n");return;}// 初始化multi句柄multiHandle = curl_multi_init();if (!multiHandle) {fprintf(stderr, "Curl multi初始化失败。\n");curl_global_cleanup();return;}// 为响应数据分配内存chunk.memory = malloc(1);chunk.size = 0;if (!chunk.memory) {fprintf(stderr, "内存分配失败。\n");curl_multi_cleanup(multiHandle);curl_global_cleanup();return;}// 初始化请求并设置选项run_curl = InitCurlRequest(curl_url, &chunk);if (!run_curl) {fprintf(stderr, "初始化curl请求失败。\n");free(chunk.memory);curl_multi_cleanup(multiHandle);curl_global_cleanup();return;}// 将curl句柄添加到multi句柄res = curl_multi_add_handle(multiHandle, run_curl);if (res != CURLM_OK) {fprintf(stderr, "将句柄添加到multi句柄失败: %s\n", curl_multi_strerror(res));free(chunk.memory);curl_easy_cleanup(run_curl);curl_multi_cleanup(multiHandle);curl_global_cleanup();return;}// 执行请求do {res = curl_multi_perform(multiHandle, &still_running);if (res != CURLM_OK) {fprintf(stderr, "curl multi执行错误: %s\n", curl_multi_strerror(res));break;}} while (still_running);// 解析响应cJSON *root = cJSON_Parse(chunk->memory);if(!root) goto end;if(parse_ip_json_str(root) == 0) {print_ip_json_parse();}cJSON_Delete(root);end:// 清理并释放资源curl_multi_remove_handle(multiHandle, run_curl);curl_easy_cleanup(run_curl);curl_multi_cleanup(multiHandle);curl_global_cleanup();// 释放分配的内存if(chunk.memory) {free(chunk.memory);chunk.memory = NULL;chunk.size = 0;}
}int main() {mulit_get_ip_info(CURL_IP_API);return 0;
}

改进:该示例中未对mulit_get_ip_info进行返回值判断,后续建议添加。

若有错误,麻烦指正~

相关文章:

  • volka 25个短语动词
  • 江科大读写内部flash到hal库实现
  • 筛选企业订单和业绩显著增长的标的
  • c语言 封装跨平台线程头文件
  • Vue:Form正则校验
  • Redis知识
  • 【大厂机试题解法笔记】最差产品奖
  • 通道注意力
  • win11部署suna
  • OpenLayers 分屏对比(地图联动)
  • OpenVINO环境配置--OpenVINO安装
  • Vue 渲染 Markdown 文件完全指南
  • 【VLNs篇】06:SmartWay—面向零样本视觉语言导航的增强路径点预测与回溯
  • 【动手学深度学习】2.6. 概率
  • C++递归语句完全指南:从原理到实践
  • 智能仓储解决方案:如何为您的企业选择最佳系统 (提升效率 降低成本)
  • 麒麟系统安装Navicat(14试用,删除文件可接着用)
  • KuiperInfer跟学第二课——张量的构建与实现
  • 嵌入式学习之系统编程(十一)网络编程之协议头,测试命令及工具
  • python闭包与装饰器
  • 网站推广/中国的搜索引擎有哪些
  • 为什么很少用python做网站/seo怎么刷关键词排名
  • 做网站的公司哪些靠谱/国内做seo最好的公司
  • 简述电子商务的含义/seo排名技术软件
  • 做转运网站/浙江网络科技有限公司
  • 上海建设工程交易中心网站/百度竞价推广登录