借助libcurl实现C++客户端请求

最近有个需求是C++作为客户端请求服务器,获取返回结果,查找了一些库C++网络库,其中mongoose只有一个.h头文件和一个.c源文件,比较方便且轻量化,客户端上传文件可以跑通,但是上传json数据时需要自己编写报文头,自己不太懂网络这一块,所以最终采用了curl库。

一、libcurl库编译安装

curl功能比较完善,可以根据自身功能需求进行编译,我这里只需要作为客户端请求服务端,所以编译的时候去掉了很多东西,这样作为第三方库依赖也会相应减少。

1.1 linux编译

如果直接git拉取的代码,需要先运行buildconf生成./configure文件

  • step1
$ ./configure --prefix=/root/package/curl_build --disable-shared --disable-thread --without-ssl --disable-ldap --disable-ldaps --without-zlib

如果产生以下错误

configure: error: cannot find install-sh

则需要安装以下内容

sudo apt-get install automake autoconf libtool

然后执行autoreconf -vif,执行完后再参照step1进行

  • step2
$ make
  • step3
$ make install

在/root/package/curl_build目录下的bin文件中,运行./curl --version查看编译后的库支持的功能,
运行./curl-config --static-libs或者ldd curl查看依赖的库文件

1.2 windows编译

参照winbuild目录下的README.md文件,如果直接从git拉取的源码,首先需要运行buildconf.bat,生成\src\tool_hugehelp.c以及Makefile文件
否则会报以下错误

error:
Nmake: fatal error u1073: Don't know how to make ".. src\tool_ hugehelp.c”
NMAKE : fatal error U1077: ""C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Tools\MSVC\14.28.29333\bin\HostX86\x86\nmake.exe"": fatal error "0x2" Stop.
  • step1
$ cd curl-7.70.0/winbuild
  • step2
    select vs2019 x64 Native Tools Command Prompt for vs 2019 with manager(管理员身份打开)
nmake /f Makefile.vc mode=static VC=15 MACHINE=x64 DEBUG=no ENABLE_WINSSL=no ENABLE_IDN=no ENABLE_SSPI=no
  • step3
    返回curl根目录,找到builds目录,
    选择 libcurl-vc15-x64-release-static-ipv6,点击可以看到bin, include以及lib三个文件

在bin目录下,运行./curl.exe --version查看编译后的库支持的功能,
利用Dependency Walker工具可查看依赖的库文件

二、使用文档

https://curl.se/docs/install.html
https://curl.se/libcurl/c/libcurl.html
https://catonmat.net/cookbooks/curl

三、代码实现

详细代码见github

常见问题
我这里编译的是静态库,集成在工程中编译时会出现undefined reference的错误
解决方式
在curl.h文件开头添加

#ifdef _WIN32
#define NOMINMAX
#define CURL_STATICLIB
#endif 

或者在调用时用extern C

extern "C"
{
#include "curl.h"
}
点击展开mongoose部分代码
// Copyright (c) 2021 Cesanta Software Limited
// All rights reserved
//
// Example HTTP client. Connect to `s_url`, send request, wait for a response,
// print the response and exit.
// You can change `s_url` from the command line by executing: ./example YOUR_URL
//
// To enable SSL/TLS for this client, build it like this:
//    make MBEDTLS=/path/to/your/mbedtls/installation
//    make OPENSSL=/path/to/your/openssl/installation
//#include <string>
//#include <stdio.h>
#include "mongoose.h"
#include "mjson.h"

// The very first web page in history. You can replace it from command line
//static const char* s_url = "http://localhost:8080/world";   // Send GET request
static const char* s_url = "http://192.168.102.116:8000/mesh/recognize";     // Send Json data

// Print HTTP response and signal that we're done
static void fn(struct mg_connection* c, int ev, void* ev_data, void* fn_data) {
    if (ev == MG_EV_CONNECT) {
        // Connected to server. Extract host name from URL
        struct mg_str host = mg_url_host(s_url);

        // Send GET request
        /*mg_printf(c,
            "GET %s HTTP/1.0\r\n"
            "Host: %.*s\r\n"
            "\r\n",
            mg_url_uri(s_url), (int)host.len, host.ptr);*/

        // Send Json data
        char* str = NULL;
        mjson_printf(mjson_print_dynamic_buf, &str, "{%Q:%Q, %Q:%Q}", "file_path", 
            "E:/code/python_web/MeshCNN/test_models/test.obj", 
            "filename", "test.obj");

        mg_printf(c, 
            "POST %s HTTP/1.0\r\n"
            "Host: %.*s\r\n"
            "Content-Type: application/json; charset=utf-8\r\n"
            "Content-Length: %lu\r\n"
            "\r\n"
            "%s",
            mg_url_uri(s_url), (int)host.len, host.ptr, (unsigned long)strlen(str), str);
        free(str);

         // If you want to upload a file, create a form upload request like this:
         const char* model_path = "E:/code/python_web/MeshCNN/test_models/test.obj";
         const char* model_name = "test.obj";
         size_t file_size = mg_file_size(model_path);
         char *file_data = mg_file_read(model_path);
         const char* boundary = "----BoundaryNetworkf912decMA4YWxkTrZu0gW";
         char tmp[256];
         snprintf(tmp, sizeof(tmp), 
             "%s\r\n"
             "Content-Disposition: form-data; name=file; filename=%s\r\n"
             "Content-Type: application/octet-stream\r\n"
             "\r\n",
             boundary, model_name);
         printf("file_size: %u\n", file_size);

         mg_printf(c,
             "POST %s?name=%s&offset=%d HTTP/1.1\r\n"
             "Host: %.*s\r\n"
             "Content-Length: %lu\r\n"
             "Content-Type: multipart/form-data; boundary=%s\r\n"
             "Accept: */*\r\n"
             "Accept-Encoding: gzip, deflate\r\n"
             "\r\n",
             mg_url_uri(s_url), model_name, file_size, (int)host.len, host.ptr,
             (unsigned long)(file_size + strlen(tmp) + strlen(boundary) + 2),
             boundary);
         mg_printf(c, "%s", tmp);
         mg_send(c, file_data, file_size);
         mg_printf(c, "%s\r\n", boundary);
         free(file_data);
    }
    else if (ev == MG_EV_HTTP_MSG) {
        // Response is received. Print it
        struct mg_http_message* hm = (struct mg_http_message*)ev_data;
        printf("********** start **********\n");
        printf("%s\n", hm->body.ptr);
        printf("********** end   **********\n");
        c->is_closing = 1;         // Tell mongoose to close this connection
        *(bool*)fn_data = true;  // Tell event loop to stop
    }
    else if (ev == MG_EV_ERROR) {
        *(bool*)fn_data = true;  // Error, tell event loop to stop
    }  
}

int main(int argc, char* argv[]) {
    struct mg_mgr mgr;                                            // Event manager
    bool done = false;                                            // Event handler flips it to true
    if (argc > 1) s_url = argv[1];                                // Use URL from command line
    mg_log_set("3");                                              // Set to 0 to disable debug
    mg_mgr_init(&mgr);                                            // Initialise event manager
    mg_http_connect(&mgr, s_url, fn, &done);                      // Create client connection
    while (!done) mg_mgr_poll(&mgr, 1000);                        // Infinite event loop
    mg_mgr_free(&mgr);                                            // Free resources
    return 0;
}

参考资料

https://curl.se/docs/install.html
https://blog.csdn.net/px41834/article/details/81627170
https://blog.csdn.net/cym1990/article/details/79851039
https://my.oschina.net/u/4324616/blog/4326368
https://www.jianshu.com/p/eef34d40f51c
https://blog.csdn.net/u014664846/article/details/91411997
https://blog.csdn.net/sinat_29891353/article/details/72526173
https://blog.csdn.net/qq_41482046/article/details/93379733
https://blog.csdn.net/giveaname/article/details/107388468

posted @   半夜打老虎  阅读(990)  评论(0编辑  收藏  举报
编辑推荐:
· AI与.NET技术实操系列:基于图像分类模型对图像进行分类
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
阅读排行:
· 25岁的心里话
· 闲置电脑爆改个人服务器(超详细) #公网映射 #Vmware虚拟网络编辑器
· 零经验选手,Compose 一天开发一款小游戏!
· 因为Apifox不支持离线,我果断选择了Apipost!
· 通过 API 将Deepseek响应流式内容输出到前端
点击右上角即可分享
微信分享提示