逼格提起来,使用curl发送网络请求

Android的网络请求

Android开发的初学者大部分都是基于Retrofit+Okhttp发送的网络请求。今天我带大家使用NDK底层代码来发送网络请求,提升应用的安全性。当然要做到真正的安全还要结合一些其他的手段,比如防调试、防静态分析(比如隐藏函数符号)、防动态注入、防接口劫持、防逆向破解(比如对接口请求进行加密)等。今天我们要用到的库是cURL库。

编译curl库

下载和安装

MacOS

我先使用MacBook安装测试一下。

sh 复制代码
git clone https://github.com/curl/curl.git
cd curl

我们可以从Github下载curl的源代码。

然后使用Homebrew包管理器安装所有需要的库。

sh 复制代码
brew install autoconf automake libtool openssl

安装过程一般不会有什么坑。 然后我们创建一个构建文件夹,命名为build,执行cmake命令试一下。报以下错误,说明我们少下载了依赖库。

sh 复制代码
mkdir build
cd build
cmake .. -DCMAKE_BUILD_TYPE=Release -DCURL_USE_OPENSSL=ON

那就给它一个libpsl库,安装,然后再次配置cmake编译脚本。

sh 复制代码
brew install libpsl
cmake .. -DCMAKE_BUILD_TYPE=Release -DCURL_USE_OPENSSL=ON

这里提一嘴CMake的编译模式,用于指定编译器编译出来的是动态库还是静态库,包不包含调试信息等。

常见的CMake 编译模式

模式 描述
Debug 含有调试信息,适用于开发调试,默认不开启优化。
Release 适用于正式发布,启用优化(-O3 )。
RelWithDebInfo 结合 ReleaseDebug,优化代码但保留调试信息。
MinSizeRel 适用于嵌入式或小型二进制,优化大小(-Os)。

配置好后,我们执行make命令开始构建。

sh 复制代码
make -j$(nproc)

可以看到构建成功了。接下来安装到电脑上。

sh 复制代码
make install

最后我们测试安装成功没有。

sh 复制代码
curl --version

查看如果正确显示版本号,就说明我们的电脑成功安装了curl。

Linux

但是我们Android开发编译Mac的dylib动态库可以吗?肯定是不行的啊。安卓底层是Linux内核,只能打.so动态库或.a的静态库。

Linux的话我要把我的云服务器用上了,我跑Java服务端程序一般用的是CentOS,就不单独再开一台Ubuntu来编译动态库了,反正也可以用。这次我打算使用官方的压缩包进行编译。

sh 复制代码
wget https://curl.se/download/curl-8.2.1.tar.gz
tar -xvzf curl-8.2.1.tar.gz
cd curl-8.2.1

下载和解压curl包。

cURL官网的服务器带宽有点慢,稍安勿躁,等不及也可以从Github下载。

sh 复制代码
yum install -y build-essential autoconf libtool pkg-config

这里注意下Linux也分版本的,我是用的CentOS版本的Linux,如果用的Ubuntu版本的Linux,则要用apt-get install命令。

同样的,也要安装一些依赖库。

sh 复制代码
yum install -y cmake
yum install -y openssl-devel
yum install -y epel-release
yum install -y cmake3

如果 cmake3 安装成功,你可以用它替换 cmake,需要注意当前版本的curl需要用cmake3进行编译,否则会失败。

sh 复制代码
sudo ln -sf /usr/bin/cmake3 /usr/bin/cmake
sh 复制代码
./configure --with-openssl

使用configure命令配置cmake脚本,需要支持openssl,因为加解密需要用到openssl。后面openssl也是要打成so文件的。

那么我们就不将curl编译成动态库了,而是编译成静态库。这样我们业务相关的底层代码可以和静态库打成一个动态库。

那么怎么编译静态库呢?其实很简单,有两种方式配置。

CMake cmake .. -DBUILD_SHARED_LIBS=OFF
configure ./configure --disable-shared --enable-static

我们就使用cmake命令配置吧。

sh 复制代码
cmake .. -DCMAKE_BUILD_TYPE=Release -DBUILD_SHARED_LIBS=OFF

把构建共享库(动态库)关掉即可。然后执行make命令进行编译。

sh 复制代码
make -j$(nproc)

可以看到我们已经成功生成了.a静态库文件。

代码中引入静态库

在CMakeList.txt文件中加入以下代码将静态库参与编译,这两行代码是一组代码,不要单独使用,使用了IMPORTED,就要使用set_target_propertiesset_target_properties用来指定库的路径。

sh 复制代码
add_library(curl
        STATIC
        IMPORTED)
set_target_properties(curl
        PROPERTIES IMPORTED_LOCATION
        ${CMAKE_CURRENT_SOURCE_DIR}/curl/libs/${ANDROID_ABI}/libcurl.a
)

最后链接库的时候像这样即可。

sh 复制代码
# 链接库
target_link_libraries(
        ${CMAKE_PROJECT_NAME}
        crypto
        ssl
        curl #curl依赖于zlib和openssl
        z
        android
        log
)

使用curl发送网络请求

GET请求

cpp 复制代码
#include <iostream>
#include <sstream>
#include <curl/curl.h>
#include <json/json.h>

size_t WriteCallback(char* ptr, size_t size, size_t nmemb, void* userdata) {
    ((std::string*)userdata)->append(ptr, size * nmemb);
    return size * nmemb;
}

void SendGetRequest(const std::string& param1, const std::string& param2) {
    CURL *curl;
    CURLcode res;
    std::string readBuffer;

    curl = curl_easy_init();
    if (curl) {
        std::string url = "https://yourproject.com/getApi?param1="
                          + param1 + "&param2=" + param2;

        curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
        curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteCallback);
        curl_easy_setopt(curl, CURLOPT_WRITEDATA, &readBuffer);
        curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L); // 关闭 SSL 验证(仅用于测试)
        curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0L);

        res = curl_easy_perform(curl);
        curl_easy_cleanup(curl);

        if (res != CURLE_OK) {
            std::cerr << "Request Error: " << curl_easy_strerror(res) << std::endl;
        }

        // 解析 JSON 响应
        Json::Value root;
        Json::CharReaderBuilder reader;
        std::string errors;
        std::istringstream s(readBuffer);
        if (!Json::parseFromStream(reader, s, &root, &errors)) {
            std::cerr << "JSON Parse Error: " << errors << std::endl;
        }

        // 开始解析json数据
        if (root["field1"].asString() == "ok" && root["field2"].isObject()) {
            Json::Value field2 = root["field2"];
            int field3 = field2["field3"].asInt();
            std::string field4 = field2["field4"].asString();
        }
    }
}

当然这之前还要引入jsoncpp的头文件。

sh 复制代码
# 目录扫描
file(GLOB_RECURSE SOURCE_CPP_FILES_OF_JSONCPP "${CMAKE_CURRENT_SOURCE_DIR}/jsoncpp/*.cpp"
        "${CMAKE_CURRENT_SOURCE_DIR}/jsoncpp/*.h"
        "${CMAKE_CURRENT_SOURCE_DIR}/jsoncpp/*.inl")
        
add_library(
    ${CMAKE_PROJECT_NAME} SHARED
    # 这里省略你业务的cpp文件若干行
    ${SOURCE_CPP_FILES_OF_JSONCPP}
)

POST请求

cpp 复制代码
#include <iostream>
#include <sstream>
#include <curl/curl.h>
#include <json/json.h>

// 回调函数
size_t WriteCallback(char *ptr, size_t size, size_t nmemb, void *userdata) {
    ((string *) userdata)->append(ptr, size * nmemb);
    return size * nmemb;
}

void SendPostRequest() {
    CURL *curl;
    CURLcode res;
    std::string readBuffer;
    curl = curl_easy_init();
    if (curl) {
        std::string url = "https://yourproject.com/postApi";
        Json::Value json;
        json["key1"] = "value1";
        json["key2"] = "value2";
        Json::FastWriter fwriter;
        std::string body = fwriter.write(json);
        std::string response;
        // 设置请求头
        struct curl_slist *headers = nullptr;
        headers = curl_slist_append(headers, "Content-Type:application/json");
        // headers = curl_slist_append(headers, "Content-Type:application/x-www-form-urlencoded");
        curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
        // 设置请求为post请求
        curl_easy_setopt(curl, CURLOPT_POST, 1);
        // 设置post请求的参数
        curl_easy_setopt(curl, CURLOPT_POSTFIELDS, body.c_str());
        /////////////////通用请求////////////////
        // 设置请求的URL地址
        curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
        // 设置数据接收函数
        curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteCallback);
        curl_easy_setopt(curl, CURLOPT_WRITEDATA, &response);
        // 进行网络操作
        res = curl_easy_perform(curl);
        /////////////////数据响应////////////////
        // 查询HTTP状态码
        long http_code = 0;
        curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &http_code);
        
        if (res != CURLE_OK) { 
            std::cerr << "Request Error: " << curl_easy_strerror(res) << std::endl; 
        }
        
        // 解析 JSON 响应
        Json::Value root;
        Json::CharReaderBuilder reader;
        std::string errors;
        std::istringstream s(readBuffer);
        if (!Json::parseFromStream(reader, s, &root, &errors)) {
            std::cerr << "JSON Parse Error: " << errors << std::endl;
        }

        // 开始解析json数据
        if (root["field1"].asString() == "ok" && root["field2"].isObject()) {
            Json::Value field2 = root["field2"];
            int field3 = field2["field3"].asInt();
            std::string field4 = field2["field4"].asString();
        }
    }
}

写在最后

那么使用NDK底层发送网络请求的大致流程就是这样了,如果有用到openssl,则自行编译成so文件引入即可。使用curl+jsoncpp+openssl可以开发高安全性的接口请求应用,具有很高的安全性和很强的隐蔽性,希望你不要拿来干坏事,干坏事也千万别说我教你的,哈哈。

相关推荐
珊瑚里的鱼5 分钟前
【双指针】专题:LeetCode 202题解——快乐数
开发语言·c++·笔记·算法·leetcode·职场和发展
火柴就是我9 分钟前
git rebase -i 修改某次提交的message
android
zhangphil21 分钟前
Android ExifInterface rotationDegrees图旋转角度,Kotlin
android·kotlin
火柴就是我25 分钟前
需求开发提交了几个 commit,提交 master 领导 review 后,说你第一笔 commit 代码有问题,让你改一下,怎么办?
android
2401_858286111 小时前
CD27.【C++ Dev】类和对象(18)友元和内部类
开发语言·c++·类和对象
(王子变青蛙)1 小时前
C++初始
开发语言·c++·程序人生
莫有杯子的龙潭峡谷1 小时前
4.15 代码随想录第四十四天打卡
c++·算法
灋✘逞_兇1 小时前
快速幂+公共父节点
数据结构·c++·算法·leetcode
KdanMin1 小时前
Android系统通知机制深度解析:Framework至SystemUI全链路剖析
android
姜行运2 小时前
每日算法(双指针算法)(Day 1)
c++·算法·c#