网络开发加速器:libcurl高效编程与跨平台部署

简介

libcurl是一个功能强大、使用广泛的开源网络传输库,支持多种协议,包括HTTP、HTTPS、FTP、FTPS、SCP、SFTP、TFTP、TELNET、DICT、FILE和LDAP等。作为一个客户端URL传输库,libcurl提供了简单易用的API,使开发者能够轻松地在自己的应用程序中集成网络功能。

libcurl的主要特点:

  • 多协议支持:一个库支持多种网络协议
  • 可移植性:支持几乎所有操作系统平台
  • 线程安全:多线程环境下可靠工作
  • 高性能:异步支持和并行传输
  • 成熟稳定:经过多年发展和广泛应用的验证
  • SSL支持:提供安全传输功能
  • 丰富的API:提供简单和高级的接口满足不同需求

libcurl的基本概念

在深入学习libcurl之前,我们需要了解一些核心概念:

1. Handle(句柄)

libcurl的API是基于句柄的,主要有以下几种:

  • Easy句柄:用于单个传输操作
  • Multi句柄:用于管理多个同时进行的传输
  • Share句柄:用于在多个Easy句柄之间共享数据

2. 接口类型

libcurl提供了两种主要的接口类型:

  • Easy接口:简单、同步、阻塞式的API,适合单个传输
  • Multi接口:复杂但更灵活的API,支持同时进行多个非阻塞传输

3. 回调函数

libcurl使用回调机制处理数据传输:

  • 写入回调:接收从服务器下载的数据
  • 读取回调:提供上传到服务器的数据
  • 进度回调:报告传输进度
  • 头部回调:处理接收到的HTTP头信息

环境搭建

Windows环境

  1. 下载预编译的库

    访问 curl官网下载页面,下载Windows版本的开发包。

  2. 使用vcpkg安装

    vcpkg install curl
    
  3. 配置Visual Studio

    • 添加包含目录:$(CURL_ROOT)\include
    • 添加库目录:$(CURL_ROOT)\lib
    • 链接库:libcurl.lib (或libcurl_d.lib用于调试版本)

Linux环境

使用包管理器安装开发包:

# Debian/Ubuntu
sudo apt-get install libcurl4-openssl-dev

# CentOS/RHEL
sudo yum install libcurl-devel

# Arch Linux
sudo pacman -S curl

macOS环境

使用Homebrew安装:

brew install curl

从源码编译

  1. 下载源码:

    git clone https://github.com/curl/curl.git
    cd curl
    
  2. 配置和编译:

    ./buildconf
    ./configure --with-ssl
    make
    sudo make install
    

基本使用方法

Easy接口

Easy接口是libcurl最简单的使用方式,适合单个URL的传输操作。下面是一个基本的HTTP GET请求示例:

#include <stdio.h>
#include <curl/curl.h>

// 回调函数,处理接收到的数据
size_t write_callback(void *ptr, size_t size, size_t nmemb, void *userdata) {
    size_t real_size = size * nmemb;
    printf("%.*s", (int)real_size, (char*)ptr);
    return real_size;
}

int main(void) {
    CURL *curl;
    CURLcode res;

    // 初始化libcurl
    curl_global_init(CURL_GLOBAL_DEFAULT);
    
    // 创建一个easy句柄
    curl = curl_easy_init();
    if(curl) {
        // 设置URL
        curl_easy_setopt(curl, CURLOPT_URL, "https://example.com");
        
        // 设置回调函数
        curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_callback);
        
        // 执行请求
        res = curl_easy_perform(curl);
        
        // 检查错误
        if(res != CURLE_OK)
            fprintf(stderr, "curl_easy_perform() failed: %s\n", curl_easy_strerror(res));
        
        // 清理句柄
        curl_easy_cleanup(curl);
    }
    
    // 全局清理
    curl_global_cleanup();
    
    return 0;
}

编译命令:

gcc -o http_get http_get.c -lcurl

发送HTTP POST请求

#include <stdio.h>
#include <curl/curl.h>

int main(void) {
    CURL *curl;
    CURLcode res;
    
    curl_global_init(CURL_GLOBAL_ALL);
    curl = curl_easy_init();
    
    if(curl) {
        // POST数据
        const char *postdata = "name=小明&age=25";
        
        curl_easy_setopt(curl, CURLOPT_URL, "https://postman-echo.com/post");
        curl_easy_setopt(curl, CURLOPT_POSTFIELDS, postdata);
        
        // 执行请求
        res = curl_easy_perform(curl);
        if(res != CURLE_OK)
            fprintf(stderr, "curl_easy_perform() failed: %s\n", curl_easy_strerror(res));
            
        curl_easy_cleanup(curl);
    }
    
    curl_global_cleanup();
    return 0;
}

Multi接口

Multi接口允许在单线程中同时执行多个传输操作,非常适合需要并行请求的场景:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <curl/curl.h>

// 用于存储每个传输的数据
struct transfer_data {
    char *memory;
    size_t size;
};

// 写入回调
static size_t write_callback(void *contents, size_t size, size_t nmemb, void *userp) {
    size_t realsize = size * nmemb;
    struct transfer_data *mem = (struct transfer_data *)userp;
    
    char *ptr = realloc(mem->memory, mem->size + realsize + 1);
    if(ptr == NULL) return 0;  // 内存不足
    
    mem->memory = ptr;
    memcpy(&(mem->memory[mem->size]), contents, realsize);
    mem->size += realsize;
    mem->memory[mem->size] = 0;
    return realsize;
}

int main(void) {
    CURL *handles[2];
    CURLM *multi_handle;
    CURLcode res;
    CURLMcode mres;
    int still_running = 1;
    struct transfer_data data[2] = {{NULL, 0}, {NULL, 0}};
    
    // 初始化
    curl_global_init(CURL_GLOBAL_DEFAULT);
    
    // 准备两个easy句柄
    handles[0] = curl_easy_init();
    handles[1] = curl_easy_init();
    
    // 初始化数据缓冲区
    data[0].memory = malloc(1);
    data[0].size = 0;
    data[1].memory = malloc(1);
    data[1].size = 0;
    
    // 设置第一个easy句柄
    curl_easy_setopt(handles[0], CURLOPT_URL, "https://example.com/");
    curl_easy_setopt(handles[0], CURLOPT_WRITEFUNCTION, write_callback);
    curl_easy_setopt(handles[0], CURLOPT_WRITEDATA, (void *)&data[0]);
    
    // 设置第二个easy句柄
    curl_easy_setopt(handles[1], CURLOPT_URL, "https://example.org/");
    curl_easy_setopt(handles[1], CURLOPT_WRITEFUNCTION, write_callback);
    curl_easy_setopt(handles[1], CURLOPT_WRITEDATA, (void *)&data[1]);
    
    // 创建multi句柄并添加easy句柄
    multi_handle = curl_multi_init();
    curl_multi_add_handle(multi_handle, handles[0]);
    curl_multi_add_handle(multi_handle, handles[1]);
    
    // 执行传输
    do {
        int numfds;
        mres = curl_multi_perform(multi_handle, &still_running);
        
        if(still_running) {
            /* 等待活动,超时或"错误" */
            mres = curl_multi_wait(multi_handle, NULL, 0, 1000, &numfds);
        }
        
        if(mres != CURLM_OK) {
            fprintf(stderr, "curl_multi_wait() failed, code %d.\n", mres);
            break;
        }
    } while(still_running);
    
    // 输出结果
    printf("First URL content size: %zu bytes\n", data[0].size);
    printf("Second URL content size: %zu bytes\n", data[1].size);
    
    // 清理
    curl_multi_remove_handle(multi_handle, handles[0]);
    curl_multi_remove_handle(multi_handle, handles[1]);
    curl_multi_cleanup(multi_handle);
    
    curl_easy_cleanup(handles[0]);
    curl_easy_cleanup(handles[1]);
    
    free(data[0].memory);
    free(data[1].memory);
    
    curl_global_cleanup();
    
    return 0;
}

常见应用场景

1. REST API调用

使用libcurl调用REST API是非常常见的应用场景。以下是一个调用JSON API的例子:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <curl/curl.h>

struct memory {
    char *response;
    size_t size;
};

static size_t cb(void *data, size_t size, size_t nmemb, void *clientp) {
    size_t realsize = size * nmemb;
    struct memory *mem = (struct memory *)clientp;
    
    char *ptr = realloc(mem->response, mem->size + realsize + 1);
    if(ptr == NULL) return 0;
    
    mem->response = ptr;
    memcpy(&(mem->response[mem->size]), data, realsize);
    mem->size += realsize;
    mem->response[mem->size] = 0;
    
    return realsize;
}

int main(void) {
    CURL *curl;
    CURLcode res;
    struct memory chunk = {0};
    
    chunk.response = malloc(1);
    chunk.size = 0;
    
    curl_global_init(CURL_GLOBAL_ALL);
    curl = curl_easy_init();
    
    if(curl) {
        struct curl_slist *headers = NULL;
        headers = curl_slist_append(headers, "Accept: application/json");
        headers = curl_slist_append(headers, "Content-Type: application/json");
        headers = curl_slist_append(headers, "charsets: utf-8");
        
        curl_easy_setopt(curl, CURLOPT_URL, "https://api.example.com/users");
        curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
        curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, cb);
        curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)&chunk);
        
        res = curl_easy_perform(curl);
        
        if(res != CURLE_OK) {
            fprintf(stderr, "curl_easy_perform() failed: %s\n", curl_easy_strerror(res));
        } else {
            printf("API Response:\n%s\n", chunk.response);
        }
        
        curl_slist_free_all(headers);
        curl_easy_cleanup(curl);
    }
    
    free(chunk.response);
    curl_global_cleanup();
    
    return 0;
}

2. 文件下载

使用libcurl下载文件到本地:

#include <stdio.h>
#include <curl/curl.h>

static size_t write_data(void *ptr, size_t size, size_t nmemb, void *stream) {
    size_t written = fwrite(ptr, size, nmemb, (FILE *)stream);
    return written;
}

int main(void) {
    CURL *curl;
    FILE *fp;
    CURLcode res;
    const char *url = "http://example.com/file.zip";
    const char *outfilename = "downloaded_file.zip";
    
    curl = curl_easy_init();
    if (curl) {
        fp = fopen(outfilename, "wb");
        
        curl_easy_setopt(curl, CURLOPT_URL, url);
        curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_data);
        curl_easy_setopt(curl, CURLOPT_WRITEDATA, fp);
        curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L);
        curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L);
        
        // 添加进度显示
        curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 0L);
        
        res = curl_easy_perform(curl);
        
        curl_easy_cleanup(curl);
        fclose(fp);
        
        if(res != CURLE_OK)
            fprintf(stderr, "下载失败: %s\n", curl_easy_strerror(res));
        else
            printf("文件已下载到 %s\n", outfilename);
    }
    
    return 0;
}

3. HTTPS安全连接

以下是一个使用HTTPS协议并验证证书的例子:

#include <stdio.h>
#include <curl/curl.h>

int main(void) {
    CURL *curl;
    CURLcode res;
    
    curl_global_init(CURL_GLOBAL_DEFAULT);
    
    curl = curl_easy_init();
    if(curl) {
        curl_easy_setopt(curl, CURLOPT_URL, "https://example.com");
        
        /* 设置CA证书路径 */
        curl_easy_setopt(curl, CURLOPT_CAINFO, "/path/to/ca-bundle.crt");
        
        /* 验证对方证书 */
        curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 1L);
        curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 2L);
        
        res = curl_easy_perform(curl);
        
        if(res != CURLE_OK)
            fprintf(stderr, "curl_easy_perform() failed: %s\n", curl_easy_strerror(res));
            
        curl_easy_cleanup(curl);
    }
    
    curl_global_cleanup();
    return 0;
}

跨平台编译指南

为ESP32交叉编译

ESP32是一种流行的IoT开发板,我们可以为其编译libcurl以实现网络功能。以下是具体步骤:

1. 安装ESP-IDF

ESP-IDF是乐鑫官方的ESP32开发框架,首先需要安装它:

git clone --recursive https://github.com/espressif/esp-idf.git
cd esp-idf
./install.sh
. ./export.sh
2. 准备libcurl源码

获取并准备libcurl源码:

git clone https://github.com/curl/curl.git
cd curl
3. 配置交叉编译环境

创建一个配置脚本esp32_configure.sh

#!/bin/bash

export IDF_PATH=/path/to/esp-idf
export PATH=$IDF_PATH/tools:$PATH

./configure \
  --host=xtensa-esp32-elf \
  --prefix=$PWD/install-esp32 \
  --disable-shared \
  --enable-static \
  --disable-dependency-tracking \
  --disable-manual \
  --disable-ntlm-wb \
  --disable-ldap \
  --disable-ldaps \
  --disable-rtsp \
  --disable-dict \
  --disable-telnet \
  --disable-tftp \
  --disable-pop3 \
  --disable-imap \
  --disable-smtp \
  --disable-gopher \
  --disable-smb \
  --without-zlib \
  --without-ssl \
  --without-brotli \
  --without-libidn2 \
  --without-librtmp \
  --without-libpsl \
  --disable-ipv6 \
  --disable-unix-sockets \
  --disable-verbose \
  --disable-alt-svc

给脚本添加执行权限并运行:

chmod +x esp32_configure.sh
./esp32_configure.sh
4. 编译和安装
make
make install
5. 在ESP32项目中使用

创建一个组件目录结构:

my-esp32-project/
├── components/
│   └── libcurl/
│       ├── include/
│       │   └── curl/
│       │       └── *.h (从libcurl安装目录复制)
│       ├── CMakeLists.txt
│       └── libcurl.a (从libcurl安装目录复制)
└── main/
    ├── CMakeLists.txt
    └── main.c

components/libcurl/CMakeLists.txt内容:

idf_component_register(
    INCLUDE_DIRS "include"
    PRIV_INCLUDE_DIRS ""
    PRIV_REQUIRES "lwip"
    REQUIRES ""
    LDFRAGMENTS "libcurl.lf"
)

add_prebuilt_library(libcurl "libcurl.a" REQUIRES lwip)

创建components/libcurl/libcurl.lf文件:

[mapping:libcurl]
archive: libcurl.a
entries:
    * (noflash)

main/main.c中使用libcurl:

#include "esp_system.h"
#include "esp_wifi.h"
#include "esp_event.h"
#include "nvs_flash.h"
#include "curl/curl.h"

static const char *TAG = "CURL_EXAMPLE";

// ... Wi-Fi连接代码 ...

void app_main(void) {
    // 初始化NVS
    esp_err_t ret = nvs_flash_init();
    if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) {
        ESP_ERROR_CHECK(nvs_flash_erase());
        ret = nvs_flash_init();
    }
    ESP_ERROR_CHECK(ret);
    
    // 连接Wi-Fi
    wifi_init_sta();
    
    // 使用libcurl
    CURL *curl;
    CURLcode res;
    
    curl_global_init(CURL_GLOBAL_DEFAULT);
    curl = curl_easy_init();
    
    if(curl) {
        curl_easy_setopt(curl, CURLOPT_URL, "http://example.com");
        curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L);
        
        res = curl_easy_perform(curl);
        if(res != CURLE_OK)
            ESP_LOGE(TAG, "curl_easy_perform() failed: %s", curl_easy_strerror(res));
        
        curl_easy_cleanup(curl);
    }
    
    curl_global_cleanup();
}

为其他嵌入式平台交叉编译

交叉编译libcurl的一般步骤如下:

  1. 安装交叉编译工具链
  2. 准备libcurl源码
  3. 配置编译选项
  4. 编译和安装
  5. 集成到目标项目

以下是为ARM平台交叉编译的示例:

export CROSS_COMPILE=arm-linux-gnueabihf-
export CC=${CROSS_COMPILE}gcc
export LD=${CROSS_COMPILE}ld
export AS=${CROSS_COMPILE}as
export AR=${CROSS_COMPILE}ar

./configure \
  --host=arm-linux-gnueabihf \
  --prefix=/path/to/install \
  --disable-shared \
  --enable-static

make
make install

高级功能与技巧

1. 使用Cookie

#include <stdio.h>
#include <curl/curl.h>

int main(void) {
    CURL *curl;
    CURLcode res;
    
    curl_global_init(CURL_GLOBAL_ALL);
    curl = curl_easy_init();
    
    if(curl) {
        // 设置Cookie
        curl_easy_setopt(curl, CURLOPT_URL, "http://example.com");
        curl_easy_setopt(curl, CURLOPT_COOKIE, "name=value; name2=value2");
        
        // 或者从文件加载Cookie
        curl_easy_setopt(curl, CURLOPT_COOKIEFILE, "cookies.txt");
        
        // 保存接收到的Cookie
        curl_easy_setopt(curl, CURLOPT_COOKIEJAR, "received_cookies.txt");
        
        res = curl_easy_perform(curl);
        
        if(res != CURLE_OK)
            fprintf(stderr, "curl_easy_perform() failed: %s\n", curl_easy_strerror(res));
            
        curl_easy_cleanup(curl);
    }
    
    curl_global_cleanup();
    return 0;
}

2. 设置HTTP代理

#include <stdio.h>
#include <curl/curl.h>

int main(void) {
    CURL *curl;
    CURLcode res;
    
    curl_global_init(CURL_GLOBAL_ALL);
    curl = curl_easy_init();
    
    if(curl) {
        curl_easy_setopt(curl, CURLOPT_URL, "http://example.com");
        
        // 设置HTTP代理
        curl_easy_setopt(curl, CURLOPT_PROXY, "http://proxy.example.com:8080");
        
        // 如果代理需要认证
        curl_easy_setopt(curl, CURLOPT_PROXYUSERNAME, "user");
        curl_easy_setopt(curl, CURLOPT_PROXYPASSWORD, "password");
        
        res = curl_easy_perform(curl);
        
        if(res != CURLE_OK)
            fprintf(stderr, "curl_easy_perform() failed: %s\n", curl_easy_strerror(res));
            
        curl_easy_cleanup(curl);
    }
    
    curl_global_cleanup();
    return 0;
}

3. 上传文件

#include <stdio.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <curl/curl.h>

int main(void) {
    CURL *curl;
    CURLcode res;
    struct stat file_info;
    FILE *fd;
    
    fd = fopen("localfile.bin", "rb");
    if(!fd)
        return 1;
        
    if(fstat(fileno(fd), &file_info) != 0)
        return 1;
    
    curl_global_init(CURL_GLOBAL_ALL);
    curl = curl_easy_init();
    
    if(curl) {
        curl_easy_setopt(curl, CURLOPT_URL, "ftp://example.com/upload/file.bin");
        curl_easy_setopt(curl, CURLOPT_UPLOAD, 1L);
        curl_easy_setopt(curl, CURLOPT_READDATA, fd);
        curl_easy_setopt(curl, CURLOPT_INFILESIZE_LARGE, (curl_off_t)file_info.st_size);
        
        res = curl_easy_perform(curl);
        
        if(res != CURLE_OK)
            fprintf(stderr, "curl_easy_perform() failed: %s\n", curl_easy_strerror(res));
            
        curl_easy_cleanup(curl);
    }
    
    fclose(fd);
    curl_global_cleanup();
    return 0;
}

4. WebSocket支持

从libcurl 7.86.0版本开始,libcurl提供了WebSocket支持。以下是一个简单的WebSocket客户端示例:

#include <stdio.h>
#include <string.h>
#include <curl/curl.h>

struct ws_data {
    const char *text;
    size_t remains;
};

static size_t send_callback(void *ptr, size_t size, size_t nmemb, void *userp) {
    struct ws_data *data = (struct ws_data *)userp;
    if(!data->remains)
        return 0;
        
    size_t len = size * nmemb;
    if(len > data->remains)
        len = data->remains;
        
    memcpy(ptr, data->text, len);
    data->text += len;
    data->remains -= len;
    
    return len;
}

static size_t recv_callback(void *data, size_t size, size_t nmemb, void *userp) {
    size_t len = size * nmemb;
    printf("Received %zu bytes: %.*s\n", len, (int)len, (char *)data);
    return len;
}

int main(void) {
    CURL *curl;
    CURLcode res;
    struct curl_ws_frame frame;
    struct ws_data data;
    const char *send_text = "Hello WebSocket!";
    
    data.text = send_text;
    data.remains = strlen(send_text);
    
    curl_global_init(CURL_GLOBAL_DEFAULT);
    curl = curl_easy_init();
    
    if(curl) {
        curl_easy_setopt(curl, CURLOPT_URL, "wss://echo.websocket.org");
        curl_easy_setopt(curl, CURLOPT_CONNECT_ONLY, 2L); /* WebSocket */
        
        res = curl_easy_perform(curl);
        if(res == CURLE_OK) {
            /* 连接已建立,发送WebSocket消息 */
            frame.age = CURL_WS_FRAME;
            frame.flags = CURL_WS_TEXT;
            frame.offset = 0;
            frame.len = data.remains;
            
            curl_easy_setopt(curl, CURLOPT_READFUNCTION, send_callback);
            curl_easy_setopt(curl, CURLOPT_READDATA, &data);
            
            res = curl_ws_send(curl, &frame, NULL);
            if(res != CURLE_OK) {
                fprintf(stderr, "curl_ws_send() failed: %s\n", curl_easy_strerror(res));
            }
            
            /* 接收WebSocket消息 */
            size_t nread;
            char buffer[1024];
            frame.len = sizeof(buffer);
            frame.offset = 0;
            
            res = curl_ws_recv(curl, buffer, sizeof(buffer), &nread, &frame);
            if(res != CURLE_OK) {
                fprintf(stderr, "curl_ws_recv() failed: %s\n", curl_easy_strerror(res));
            } else {
                printf("Received %zu bytes: %.*s\n", nread, (int)nread, buffer);
            }
        } else {
            fprintf(stderr, "curl_easy_perform() failed: %s\n", curl_easy_strerror(res));
        }
        
        curl_easy_cleanup(curl);
    }
    
    curl_global_cleanup();
    return 0;
}

常见问题与解决方案

1. SSL证书验证问题

问题:在HTTPS连接时出现SSL证书验证失败。

解决方案

  1. 提供CA证书路径:

    curl_easy_setopt(curl, CURLOPT_CAINFO, "path/to/ca-bundle.crt");
    
  2. 禁用证书验证(不推荐用于生产环境):

    curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L);
    

2. 内存泄漏问题

问题:使用libcurl时出现内存泄漏。

解决方案

  • 确保每次curl_easy_init()都有对应的curl_easy_cleanup()
  • 释放所有通过curl_slist_append()创建的列表
  • 释放回调函数中分配的内存
  • 使用工具如Valgrind检测内存泄漏

3. 连接超时问题

问题:在网络不稳定的环境中,连接经常超时。

解决方案
设置适当的超时参数:

curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, 10L); // 连接超时时间(秒)
curl_easy_setopt(curl, CURLOPT_TIMEOUT, 30L);        // 总操作超时时间(秒)

4. 重定向问题

问题:请求不跟随重定向。

解决方案
启用自动重定向:

curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L);
curl_easy_setopt(curl, CURLOPT_MAXREDIRS, 5L);  // 最大重定向次数

参考资源

  1. libcurl官方文档
  2. libcurl API参考
  3. libcurl教程
  4. ESP32官方文档
  5. libcurl常见问题

本文提供了libcurl库的基本介绍、安装方法、基本用法和高级应用,以及如何在各种平台上进行交叉编译。希望这些内容能够帮助您更好地使用libcurl实现各种网络功能。
关注 嵌入式软件客栈 公众号,获取更多内容
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Psyduck_ing

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值