curl获取ip定位信息 --- system(一)
本文使用curl获取ip地理位置的json格式,并通过cjson进行解析,使用curl的三种方式进行请求:
-
命令行system()将curl后的内容拷贝到文件中,后进行解析
-
使用libcurl请求
-
使用libcurl multi 异步方式请求
1.获取ip地理位置的api
常用的获取ip地理位置的api:
- ip-api.com 接口,具体使用方式查看官网
http://ip-api.com/json/{query}
- whois.pconline.com.cn 接口
http://whois.pconline.com.cn/ipJson.jsp
本文使用ip_api.com
接口,可在网页上进行demo测试,网址如下:
https://ip-api.com/docs/api:json
可以在returned date勾选需要返回的值
例如,需获取当前ip的countryCode
,regionName
,city
,lat
,lon
,则勾线相应的选项
点击demo按键,则会跳转到测试回复这里,则会显示当前回复的数据内容
2. 使用cjson进行解析
- cJson:一个基于 C 语言的 Json 库,它是一个开源项目,github 下载地址:cjosn github 地址
- cJson库组成:主要的文件有两个,一个 cJSON.c 一个 cJSON.h。使用时,将头文件
include
进去即可
本文不对cjson进行详细说明
首先将json串进行压缩转义,可使用的网址为:压缩转义json网页,将上述返回值转义为字符串,方便测试,例如:
{\"status\":\"success\",\"country\":\"Canada\",\"countryCode\":\"CA\",\"regionName\":\"Quebec\",\"city\":\"Montreal\",\"lat\":45.6026,\"lon\":-73.5167}
根据ip_api的返回值json,可进行相关解析,解析如下:
#include <stdio.h>
#include <string.h>
#include "cJSON.h"typedef struct ip_info {char countryCode[8];char regionName[32];char city[32];double lat;double lon;
} ip_info;ip_info current_ip_info = {0};int parse_ip_json_str(const char *current_json_str){int ret = -1;cJSON *tmp_json = cJSON_Parse(current_json_str);if (!tmp_json ) {const char *error_ptr = cJSON_GetErrorPtr();if (error_ptr) fprintf(stderr, "JSON解析错误: %s\n", error_ptr);return -1;}cJSON *status = cJSON_GetObjectItemCaseSensitive(tmp_json , "status");if (!cJSON_IsString(status) || strcmp(status->valuestring, "success") != 0) {ret = -1;goto end;}cJSON *item;if ((item = cJSON_GetObjectItem(tmp_json , "countryCode")) && cJSON_IsString(item)) strncpy(current_ip_info.countryCode, item->valuestring, sizeof(current_ip_info.countryCode)-1);if ((item = cJSON_GetObjectItem(tmp_json , "regionName")) && cJSON_IsString(item)) strncpy(current_ip_info.regionName, item->valuestring, sizeof(current_ip_info.regionName)-1);if ((item = cJSON_GetObjectItem(tmp_json , "city")) && cJSON_IsString(item)) strncpy(current_ip_info.city, item->valuestring, sizeof(current_ip_info.city)-1);if ((item = cJSON_GetObjectItem(tmp_json , "lat")) && cJSON_IsNumber(item)) current_ip_info.lat = item->valuedouble;if ((item = cJSON_GetObjectItem(tmp_json , "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:cJSON_Delete(tmp_json);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");
}int main() {const char json_str[] = ""{\"status\":\"success\",\"country\":\"Canada\",\"countryCode\":\"CA\",\"regionName\":\"Quebec\",\"city\":\"Montreal\",\"lat\":45.6026,\"lon\":-73.5167};if(parse_ip_json_str(json_str) == 0) {print_ip_json_parse();}
}
则打印结果为:
3. 使用curl获取api返回值
3.1 使用system指令直接获取
使用system(curl ....)
是通过调用系统的命令行来运行 curl
命令。这意味着它会启动一个外部进程来执行 curl
,并在终端或命令行环境中运行该命令。
缺点:
- 性能开销:每次调用
system(curl)
都需要启动一个新的外部进程,这会增加系统开销,影响性能; - 且若无系统调度,则会阻塞主线程的运行;
- 依赖于系统的
curl
命令:你必须依赖外部curl
工具,无法灵活控制; - 输出不方便处理:调用
system(curl)
会将结果输出到标准输出,处理结果需要额外的工作; - 安全性:如果拼接命令字符串时不小心,可能会导致安全漏洞,尤其是在传递不受信任的输入时;
则该方式适合测试时使用,或者调用次数及性能要求不高的场景需求,不推荐在正式代码中使用,本文采用静默且将输出保存到文件中,封装的函数如下;
void execute_curl(const char *url, const char *output_file) {char command[256] = {0};if (output_file) {snprintf(command, sizeof(command), "curl -s -o '%s' '%s'", output_file, url);} else {snprintf(command, sizeof(command), "curl -s '%s'", url);}system(command);
}
则使用其进行curl请求
#define TEMP_FILE_PATH "/tmp/tmp_file.json"
#define CURL_IP_API "http://ip-api.com/json/?fields=status,message,countryCode,regionName,city,lat,lon"execute_curl(CURL_IP_API, TEMP_FILE_PATH);
使用cat
指令查看,确定是保存成功的,如下
之后就可以将其从文件中读出,删除tmp文件,解析json数据,下面为将其从文件读出,并返回json数据的代码:
cJSON *parse_json_file(const char *filename) {FILE *fp = fopen(filename, "rb"); // 以二进制模式打开确保ftell正确if (!fp) return NULL;// 获取文件大小fseek(fp, 0, SEEK_END);long file_size = ftell(fp);fseek(fp, 0, SEEK_SET);// 处理空文件情况if (file_size <= 0) {fclose(fp);return NULL;}// 动态分配缓冲区char *buffer = (char*)malloc(file_size + 1);if (!buffer) {fclose(fp);return NULL;}// 读取文件内容size_t bytes_read = fread(buffer, 1, file_size, fp);fclose(fp);if (bytes_read != (size_t)file_size) {free(buffer);return NULL;}// 添加字符串终止符buffer[bytes_read] = '\0';// 解析JSONcJSON *json = cJSON_Parse(buffer);free(buffer); // 释放原始缓冲区return json;
}
可根据需求添加是否要删除临时文件
unlink(TEMP_FILE_PATH);
则将其整合起来的代码如下:
#include <stdio.h>
#include <string.h>
#include "cJSON.h"
#include <<unistd.h>#define TEMP_FILE_PATH "/tmp/tmp_file.json"
#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;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");
}cJSON *parse_json_file(const char *filename) {FILE *fp = fopen(filename, "rb"); // 以二进制模式打开确保ftell正确if (!fp) return NULL;// 获取文件大小fseek(fp, 0, SEEK_END);long file_size = ftell(fp);fseek(fp, 0, SEEK_SET);// 处理空文件情况if (file_size <= 0) {fclose(fp);return NULL;}// 动态分配缓冲区char *buffer = (char*)malloc(file_size + 1);if (!buffer) {fclose(fp);return NULL;}// 读取文件内容size_t bytes_read = fread(buffer, 1, file_size, fp);fclose(fp);if (bytes_read != (size_t)file_size) {free(buffer);return NULL;}// 添加字符串终止符buffer[bytes_read] = '\0';// 解析JSONcJSON *json = cJSON_Parse(buffer);free(buffer); // 释放原始缓冲区return json;
}int main() {execute_curl(CURL_IP_API, TEMP_FILE_PATH);cJSON *root = parse_json_file(TEMP_FILE_PATH);if (!root) {printf("parse_json_file error\r\n");unlink(TEMP_FILE_PATH)return -1;}if(parse_ip_json_str(root) == 0) {print_ip_json_parse();}cJSON_Delete(root);unlink(TEMP_FILE_PATH);return 0;
}
使用libcurl请求和使用libcurl multi 异步方式请看下篇,如有错误,请指正,谢谢~