由浅入深C系列五:使用libcurl进行基于http get/post模式的C语言交互应用开发

简介: 由浅入深C系列五:使用libcurl进行基于http get/post模式的C语言交互应用开发

简介

大多数在linux下的开发者,都会用到curl这个命令行工具。对于进行restful api的测试等,非常方便。其实,这个工具还提供了一个C的开发库,可以很方便的在C语言开发环境下完成基于http的请求和响应交互,高效的开发基于http/smtp等的网络应用程序

/* 2023-08-14 更新宏定义 
    1. 使用可变参数,支持多项输出; 
    2. 去除Z中默认加上的双引号; 
*/
#define X_LOG_DEBUG(Z, X...) \
    printf("[%s %s] [%s.%d] [%s] [DEBUG] " Z "\n", __DATE__, __TIME__, __FILE__, __LINE__, __FUNCTION__, ##X)

环境准备

下载并安装curl的开发包

yum install libcurl-devel.x86_64

在线资源

开发过程中主要参考CURL官方介绍及API参考文档 | link

示例代码

多余的话就不多说了,直接上示例代码,通过代码中的注释来说明开发过程。

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "../include/xhttp.h"

/*
-  这个是一个回调函数,主要用于curl在执行过程中,当有被请求的数据到达时,被调用来向-curl_easy_setopt设置的chunk中写入数据。
  这个回调函数在curl_easy_perform执行完成前会被调用多次。当执行完成后,从chunk中取出这次交互返回的数据。
*/
static size_t cb_write_data(void *data, size_t size, size_t nmemb, void *clientp)
{
   
    size_t realsize = size * nmemb;
    http_response *mem = (http_response *)clientp;

    char *ptr = realloc(mem->response, mem->size + realsize + 1);
    if(ptr == NULL)
        return 0;  /* out of response_st! */

    mem->response = ptr;
    memcpy(&(mem->response[mem->size]), data, realsize);
    mem->size += realsize;
    mem->response[mem->size] = 0;

    return realsize;
}

/*
  这个是向外发布的一个函数,调用的方式示例如下:
  char* payload = "{\"code\":\"\",\"codeUuid\":\"\",\"loginName\":\"user@domain\",\"loginPwd\":\"xxxxxxxx\"}";
  http_response *resp = http_post("https://local.domain/admin-api/session/login", NULL, payload);
  使用完返回数据后,记得释放resp->reesponse,避免内存漏。
*/
http_response* http_post(char* url, char* token, char* payload) 
{
   
    http_response chunk = {
   0};
    /* 设置curl上下文,对curl实例进行初始化 */
    curl_global_init(CURL_GLOBAL_ALL);
    CURL *curl = curl_easy_init();
    CURLcode res;
    if(curl) 
    {
   
        X_LOG_DEBUG("%s", "libcurl curl_easy_setopt start ...");
        /* 设置curl各个参数: 请求地址 */
        curl_easy_setopt(curl, CURLOPT_URL, url);
        /* 设置curl各个参数: 请求方式为post */
        curl_easy_setopt(curl, CURLOPT_POST, 1L);

        /* 设置curl各个参数: http中的请求头部分的内容 */
        X_LOG_DEBUG("%s", "libcurl curl_easy_setopt CURLOPT_HTTPHEADER start ...");
        struct curl_slist *list = {
   0};
        list = curl_slist_append(NULL, "Content-Type: application/json;charset=utf8");
        list = curl_slist_append(list, "routeurl: /project/project-list");
        /* 设置curl各个参数: 可选部分,如果请求中要求token,可以设置上 */
        if (token != NULL)
        {
   
            char* x_access_token_str = (char*)malloc( MAX_UTMP_TOKEN_SIZE );
            sprintf(x_access_token_str, "x-access-token: %s", token);
            X_LOG_DEBUG("%s", x_access_token_str);
            list = curl_slist_append(list, x_access_token_str);
        }
        curl_easy_setopt(curl, CURLOPT_HTTPHEADER, list);

        X_LOG_DEBUG("%s", "libcurl curl_easy_setopt CURLOPT_USERAGENT start ...");
        /* some servers do not like requests that are made without a user-agent 
            field, so we provide one */
        curl_easy_setopt(curl, CURLOPT_USERAGENT, "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:82.0) Gecko/20100101 Firefox/82.0");

        /* 设置curl各个参数: http的请求体部分,主要是请求中携带的数据 */
        /* POST data */
        X_LOG_DEBUG("%s", "libcurl curl_easy_setopt CURLOPT_POSTFIELDSIZE/CURLOPT_POSTFIELDS start ...");
        X_LOG_DEBUG("request body data is:%s", payload);
        X_LOG_DEBUG("request body len is:%d", strlen(payload));
        /* size of the POST data */
        curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, strlen(payload));
        /* pass in a pointer to the data - libcurl will not copy */
        curl_easy_setopt(curl, CURLOPT_POSTFIELDS, payload);

        /* 设置curl各个参数: 重要部分,设置了返回值的回调函数和返回值的内存放置区域 */
        /* RECEIVE DATA */
        X_LOG_DEBUG("%s", "libcurl curl_easy_setopt CURLOPT_WRITEFUNCTION/CURLOPT_WRITEDATA start ...");
        /* send all data to this function  */
        curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, cb_write_data);
        /* we pass our 'chunk' struct to the callback function */
        curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)&chunk);

        X_LOG_DEBUG("%s", "libcurl curl_easy_setopt successfully complete ...");

        /* 执行curl请求,并获取返回值:CURLE_OK,表示执行成功 */
        X_LOG_DEBUG("%s", "libcurl curl_easy_perform start ...");
        res = curl_easy_perform(curl);
        X_LOG_DEBUG("%s", "libcurl curl_easy_perform successfully complete ...");

        if(res != CURLE_OK) {
   
            fprintf(stderr, "curl_easy_perform() is failed: %s", curl_easy_strerror(res));
            curl_slist_free_all(list); /* free the list again */
            curl_easy_cleanup(curl);
        } else {
   
            /* 处理curl返回的数据 */
            X_LOG_DEBUG("chunk size %d", chunk.size);

            http_response response = {
   0};
            response.response = (char*)malloc(1048576*5);
            memset(response.response, 0, chunk.size+1);
            memcpy(response.response, chunk.response, chunk.size+1);
            response.size = chunk.size;

            /* remember to free the buffer */
            free(chunk.response);
            curl_slist_free_all(list); /* free the list again */ 
            curl_global_cleanup();

            return &response;
        }
    } 
}

引用的头文件如下:

#ifndef __X_HTTP_H__
#define __X_HTTP_H__

#include <stdlib.h>
#include <curl/curl.h>
/* 更新宏定义 1. 使用可变参数,支持多项输出; 2. 去除Z中默认加上的双引号; */
#define X_LOG_DEBUG(Z, X...) \
    printf("[%s %s] [%s.%d] [%s] [DEBUG] " Z "\n", __DATE__, __TIME__, __FILE__, __LINE__, __FUNCTION__, ##X)

#define MAX_UTMP_TOKEN_SIZE 8192

typedef struct http_response_s {
   
  char *response;
  size_t size;
} http_response;

typedef struct http_request_s {
   
    char *request;
    size_t size;
} http_request;

http_response* http_post(char* url, char* token, char* payload);

#endif

测试调用

int main(int argc, char** argv) {
   
    char* payload = "{\"code\":\"\",\"codeUuid\":\"\",\"loginName\":\"user@domain\",\"loginPwd\":\"xxxxxxxx\"}";
    http_response *resp = http_post("https://local.domain/admin-api/session/login", NULL, payload);
    printf("http_response [%d] is: %s\n", resp->size, resp->response);
    char *tokenVal = strstr(resp->response, "xaccessToken");
    int end = strlen(tokenVal);
    *(tokenVal + end-2) = 0;
    char** token_arr;
    __strtok_r(tokenVal, ":", token_arr);
    char* replacementKey = strtrim(token_arr[0], '\"');
    exit(0)
}

运行结果

[Aug 11 2023 10:20:02] [src/xhttp.c.37] [http_post] [DEBUG] "libcurl curl_easy_setopt start ..."
[Aug 11 2023 10:20:02] [src/xhttp.c.42] [http_post] [DEBUG] "libcurl curl_easy_setopt CURLOPT_HTTPHEADER start ..."
[Aug 11 2023 10:20:02] [src/xhttp.c.55] [http_post] [DEBUG] "libcurl curl_easy_setopt CURLOPT_USERAGENT start ..."
[Aug 11 2023 10:20:02] [src/xhttp.c.61] [http_post] [DEBUG] "libcurl curl_easy_setopt CURLOPT_POSTFIELDSIZE/CURLOPT_POSTFIELDS start ..."
[Aug 11 2023 10:20:02] [src/xhttp.c.62] [http_post] [DEBUG] "request body data is:{"code":"","codeUuid":"","loginName":"user@domain","loginPwd":"xxxxxxxx"}"
[Aug 11 2023 10:20:02] [src/xhttp.c.63] [http_post] [DEBUG] "request body len is:75"
[Aug 11 2023 10:20:02] [src/xhttp.c.70] [http_post] [DEBUG] "libcurl curl_easy_setopt CURLOPT_WRITEFUNCTION/CURLOPT_WRITEDATA start ..."
[Aug 11 2023 10:20:02] [src/xhttp.c.76] [http_post] [DEBUG] "libcurl curl_easy_setopt successfully complete ..."
[Aug 11 2023 10:20:02] [src/xhttp.c.79] [http_post] [DEBUG] "libcurl curl_easy_perform start ..."
[Aug 11 2023 10:20:02] [src/xhttp.c.81] [http_post] [DEBUG] "libcurl curl_easy_perform successfully complete ..."
[Aug 11 2023 10:20:02] [src/xhttp.c.90] [http_post] [DEBUG] "chunk size 6693"
相关文章
|
3月前
|
安全 网络协议 Ubuntu
【常见开源库的二次开发】HTTP之libcurl库——libcurl使用(二)
【常见开源库的二次开发】HTTP之libcurl库——libcurl使用(二)
281 2
|
4月前
|
缓存 自然语言处理 前端开发
第一章 引言-HTTP协议基础概念和前后端分离架构请求交互概述
第一章 引言-HTTP协议基础概念和前后端分离架构请求交互概述
121 0
|
28天前
|
编译器 API C语言
C语言与硬件交互:通过I/O端口控制硬件。
C语言与硬件交互:通过I/O端口控制硬件。
35 0
|
3月前
|
缓存 网络协议 安全
【常见开源库的二次开发】HTTP之libcurl库——基础知识扫盲(一)
【常见开源库的二次开发】HTTP之libcurl库——基础知识扫盲(一)
55 1
|
4月前
|
传感器 人工智能 物联网
【C 言专栏】C 语言与硬件交互的方法
【5月更文挑战第4天】C 语言在硬件交互中扮演关键角色,主要通过直接访问硬件寄存器、中断处理、I/O 端口操作、内存映射I/O和设备驱动程序开发。挑战包括硬件多样性、实时性要求和错误处理。随着物联网和人工智能发展,C语言与硬件交互的需求增加,未来将面临更多新硬件和技术的挑战。本文旨在帮助读者理解和掌握这一领域的知识,以实现更高效的硬件互动。
138 1
【C 言专栏】C 语言与硬件交互的方法
|
3月前
|
监控 小程序 前端开发
基础入门-抓包技术&HTTPS协议&WEB&封包监听&网卡模式&APP&小程序
基础入门-抓包技术&HTTPS协议&WEB&封包监听&网卡模式&APP&小程序
127 0
|
3月前
|
资源调度 分布式计算 Hadoop
实时计算 Flink版产品使用问题之yarn session模式中启动的任务链接是http IP,想把IP映射为主机hadoop,该怎么操作
实时计算Flink版作为一种强大的流处理和批处理统一的计算框架,广泛应用于各种需要实时数据处理和分析的场景。实时计算Flink版通常结合SQL接口、DataStream API、以及与上下游数据源和存储系统的丰富连接器,提供了一套全面的解决方案,以应对各种实时计算需求。其低延迟、高吞吐、容错性强的特点,使其成为众多企业和组织实时数据处理首选的技术平台。以下是实时计算Flink版的一些典型使用合集。
|
4月前
|
人工智能 算法 数据可视化
C语言”三子棋“升级版(模式选择+”模拟智能“下棋)
C语言”三子棋“升级版(模式选择+”模拟智能“下棋)
|
4月前
|
数据采集 安全 调度
【C 言专栏】C 语言与操作系统的交互
【5月更文挑战第1天】C语言在操作系统中扮演核心角色,常用于内核及系统级应用开发。它涉及进程管理(如`fork()`创建进程)、内存分配(`malloc()`、`free()`)、文件操作(`open()`、`read()`等)和设备驱动。C语言允许直接硬件交互,支持进程间通信和文件系统管理,但也带来资源管理、错误处理和可移植性的挑战。在嵌入式系统中,C语言尤为重要。随着技术发展,C语言将继续适应新操作系统和硬件架构,与其他语言融合,推进系统编程进步。
72 0
【C 言专栏】C 语言与操作系统的交互
|
4月前
|
存储 物联网 程序员
C语言与硬件交互
C语言因其低级别特性和高性能在系统编程和硬件交互中广泛应用。它提供内存操作和底层指令调用来直接控制硬件,尤其是在嵌入式系统、操作系统内核和驱动开发中。通过指针操作内存地址实现硬件寄存器的读写,或使用内联汇编调用底层指令,但要注意权限、内存映射、并发问题和硬件差异。文中还给出了简单的读写硬件状态和控制设备的C语言代码示例。在高级应用中,C语言用于编写驱动程序和实时操作系统,是底层开发的关键技能。

热门文章

最新文章