【小沐学C++】libcurl实现HTTP/HTTPS请求

1、简介

https://github.com/curl/curl
https://curl.se/libcurl/
https://curl.se/download.html

libcurl 是一个免费且易于使用的客户端 URL 传输 库,支持DICT,FILE,FTP,FTPS,GOPHER,GOPHERS,HTTP,HTTPS, IMAP, IMAPS, LDAP, LDAPS, MQTT, POPP3, POP3S, RTMP, RTMPS, RTSP, SCP, SFTP, SMB、SMBS、SMTP、SMTPS、TELNET 和 TFTP。libcurl 支持 SSL 证书,HTTP POST,HTTP PUT,FTP 上传,基于 HTTP 表单的上传, 代理、HTTP/2、HTTP/3、cookie、用户+密码身份验证(基本、 摘要,NTLM,协商,Kerberos),文件传输恢复,http代理 隧道等等!

libcurl 是高度可移植的,它可以在众多 平台,包括 Solaris、NetBSD、FreeBSD、OpenBSD、Darwin、HPUX、IRIX、 AIX, Tru64, Linux, UnixWare, HURD, Windows, Amiga, OS/2, BeOs, Mac OS X, Ultrix,QNX,OpenVMS,RISC OS,Novell NetWare,DOS等等…

在这里插入图片描述

2、下载和编译

2.1 下载

官网下载地址:
https://curl.se/download.html

  • curl executable
    您将从此链接获得预构建的“curl”二进制文件(或者在某些情况下,使用此链接带您访问的页面上提供的信息)。您可能会也可能不会将“libcurl”安装为共享库/ DLL。
  • libcurl development
    这是用于 libcurl 开发的 - 但并不总是包含 libcurl 本身。很可能是头文件和文档。如果你打算编译或构建使用 libcurl 的东西,这很可能是你想要的包。
  • libcurl
    这是一个纯二进制 libcurl 包,可能包括头文件和文档,但没有命令行工具和其他问题。如果你想让一个使用 libcurl 的程序使用 libcurl,这很可能是你想要的包。
  • source code
    -您不会从此链接下载预构建的二进制文件。相反,您将获得一个指向带有 curl 源的网站的链接,该链接已针对您的平台进行了调整。您需要设置编译器并努力工作才能从源包构建 curl。

最新源码如下图:
在这里插入图片描述

2.2 编译

解压源码压缩包如下:
在这里插入图片描述
找到windows代码工程文件,如下:在这里插入图片描述
使用vs2017打开如下:
在这里插入图片描述
编译后生成文件如下:
在这里插入图片描述

2.3 使用

使用步骤:

  1. 调用curl_global_init()初始化libcurl
  2. 调用curl_easy_init()函数得到 easy interface型指针
  3. 调用curl_easy_setopt()设置传输选项
  4. 根据curl_easy_setopt()设置的传输选项,实现回调函数以完成用户特定任务
  5. 调用curl_easy_perform()函数完成传输任务
  6. 调用curl_easy_getinfo()接口获取网络请求响应信息,
  7. 调用curl_easy_cleanup()释放内存
  8. 调用curl_global_cleanup()析构libcurl

3、命令行测试

https://curl.se/docs/manual.html

3.1 获取文件头Headers

-H/--header     LINE        (H) # 添加请求头, 可添加多个 -H 参数, 
                                # 参数格式: -H "NAME: VALUE"
 
-A/--user-agen  STRING      (H) # 请求头的 User-Agent 字段
-e/--referer    URL         (H) # 请求头的 Referer 字段
-r/--range      RANGE       (H) # 请求头的 Range 字段
-b/--cookie     STRING/FILE (H) # 请求头的 Cookie 字段, 以字符串的形式提供, 
                                # 或从指定 cookie 文件中读取
 
-c/--cookie-jar     FILE    (H) # 把响应头中的 cookie 保存到指定文件
 
-D/--dump-header    FILE        # 把 headers 信息保存指定文件
-I/--head                       # 只显示文档信息(只显示响应头)

3.2 请求内容Request Content

# 执行命令, 如果是 HTTP 则是请求方法, 如: GET, POST, PUT, DELETE 等
#          如果是 FTP 则是执行 FTP协议命令
-X/--request    COMMAND
 
# HTTP POST 请求内容(并自动发出 POST 请求), 例如: aa=bb&cc=dd
-d/--data       DATA    (H)
 
# HTTP multipart POST 表单数据,(并自动发出 POST 请求)
# 多个表单字段可添加多个 -H 参数, 如果是文件参数, 路径值前面需要加@
# 参考格式: -F "name1=@/filepath" -F "name2=stringvalue"
-F/--form       CONTENT (H)  

3.3 响应内容Response Content

-o/--output FILE    FILE        # 把响应内容输出到指定文件
-O/--remote-name                # 以 URL 的文件名作为文件名称保存响应内容到当前目录
-C/--continue-at    OFFSET      # 断点续传, 从 offset 位置继续传输

3.4 GET请求

curl https://www.baidu.com/         # GET请求, 输出 响应内容
curl -I https://www.baidu.com/      # GET请求, 只输出 响应头
curl -i https://www.baidu.com/      # GET请求, 输出 响应头、响应内容
curl -v https://www.baidu.com/      # GET请求, 输出 通讯过程、头部信息、响应内容等

下载文件
# 指定保存的文件名称下载文件
curl https://www.baidu.com -o baidu.txt
 
# 使用 URL 指定的资源文件名保存下载文件(URL 必须指向具体的文件名)
curl https://www.baidu.com/index.html -O
 
# 指定 Usaer-Agent 和 Referer 请求头的值, 下载文件
curl -A "Mozilla/5.0 Chrome/70.0.3538.110 Safari/537.36" \
     -e "https://www.baidu.com/" \
     https://www.baidu.com/index.html -O

curl -o thatpage.html http://www.example.com/
curl -O http://www.example.com/index.html
curl -O www.haxx.se/index.html -O curl.se/download.html

# 指定Authorization请求头的值, 下载文件
# 参数格式: -H "NAME: VALUE"
curl -H "Authorization: a112121dada" \
     https://www.baidu.com/index.html -O

在这里插入图片描述

3.5 POST请求

 POST 提交 JSON 数据(\表示命令语句还未结束, 换行继续)
curl -H "Content-Type: application/json"                \
     -d '{"username":"hello", "password":"123456"}'     \
     http://localhost/login
 
# POST 提交 表单数据
curl -F "username=hello"                \
     -F "password=123456"               \
     -F "head_image=@filepath.jpg"      \
     http://localhost/register

curl -d "name=Rafael%20Sagula&phone=3320780" http://www.where.com/guest.cgi
curl --data-urlencode "name=Rafael Sagula&phone=3320780" http://www.where.com/guest.cgi
curl -d "user=foobar&pass=12345&id=blablabla&ding=submit" http://www.formpost.com/getthis/post.cgi
curl -F "coolfiles=@fil1.gif;type=image/gif,fil2.txt,fil3.html" http://www.post.com/postit.cgi

3.6 其他

-h/--help                       # 打印帮助信息
-V/--version                    # 显示版本信息
-s/--silent                     # 静默模式, 不输出任何内容
-i/--include                    # 输出包含 headers 信息
-v/--verbose                    # 输出详细内容
-#/--progress-bar               # 以进度条方式显示传输过程

-y/--speed-time     SECONDS     # 连接 超时时间, 单位: 秒,  默认为 30
-m/--max-time       SECONDS     # 读取 超时时间, 必须在该时间内传输完数据, 单位: 秒
--limit-rate        RATE        # 限速传输, 单位: Byte/s
 
-x/--proxy          [PROTOCOL://]HOST[:PORT]    # 设置代理
-U/--proxy-user     USER[:PASSWORD]             # 代理的用户名和密码
-u/--user           USER[:PASSWORD][;OPTIONS]   # 设置服务器的用户密码和登录选项
--cacert            FILE                  (SSL) # 使用指定的 CA 证书
 
-P/--ftp-port       ADR (F) # 指定 FTP 传输的端口
-T/--upload-file    FILE    # 上传文件到指定的 URL (http/ftp) 位置, 
                            # 参考格式: -T "file1" 或 -T "{file1,file2}"
 
-Q/--quote    CMD  (F/SFTP) # 执行命令, -X 只执行一条命令, -Q 可执行多条,
                            # 多条命令将按顺序执行,
                            # 参考格式: -Q "cmd1" -Q "cmd2"

在这里插入图片描述

4、代码测试

打开源码的文件夹的测试示例子文件夹如下:
在这里插入图片描述

3.1 simple.c

/* <DESC>
 * Download a given URL into a local file named page.out.
 * </DESC>
 */
#include <stdio.h>
#include <stdlib.h>
//#include <unistd.h>
#include <curl/curl.h>
#pragma comment(lib, "libcurld.lib")

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

	curl = curl_easy_init();
	if (curl) {
		curl_easy_setopt(curl, CURLOPT_URL, "https://jsonplaceholder.typicode.com/todos/2");
		/* example.com is redirected, so we tell libcurl to follow redirection */
		curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L);

		/* Perform the request, res will get the return code */
		res = curl_easy_perform(curl);
		/* Check for errors */
		if (res != CURLE_OK)
			fprintf(stderr, "curl_easy_perform() failed: %s\n",
				curl_easy_strerror(res));

		/* always cleanup */
		curl_easy_cleanup(curl);
	}
	return 0;
}

在这里插入图片描述

3.2 url2file.c

#include "pch.h"
#include <iostream>

/* <DESC>
 * Download a given URL into a local file named page.out.
 * </DESC>
 */
#include <stdio.h>
#include <stdlib.h>
//#include <unistd.h>
#include <curl/curl.h>
#pragma comment(lib, "libcurld.lib")

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(int argc, char *argv[])
{
	CURL *curl_handle;
	static const char *pagefilename = "test.jpg";
	FILE *pagefile;

	if (argc < 2) {
		printf("Usage: %s <URL>\n", argv[0]);
		return 1;
	}

	curl_global_init(CURL_GLOBAL_ALL);

	/* init the curl session */
	curl_handle = curl_easy_init();

	/* set URL to get here */
	curl_easy_setopt(curl_handle, CURLOPT_URL, argv[1]);

	/* Switch on full protocol/debug output while testing */
	curl_easy_setopt(curl_handle, CURLOPT_VERBOSE, 1L);

	/* disable progress meter, set to 0L to enable it */
	curl_easy_setopt(curl_handle, CURLOPT_NOPROGRESS, 1L);

	/* send all data to this function  */
	curl_easy_setopt(curl_handle, CURLOPT_WRITEFUNCTION, write_data);

	/* open the file */
	pagefile = fopen(pagefilename, "wb");
	if (pagefile) {

		/* write the page body to this file handle */
		curl_easy_setopt(curl_handle, CURLOPT_WRITEDATA, pagefile);

		/* get it! */
		curl_easy_perform(curl_handle);

		/* close the header file */
		fclose(pagefile);
	}

	/* cleanup curl stuff */
	curl_easy_cleanup(curl_handle);

	curl_global_cleanup();

	return 0;
}

编译如下:
在这里插入图片描述

运行如下:
在这里插入图片描述
下载图片如下:
在这里插入图片描述

3.3 simplepost.c

/* <DESC>
 * Very simple HTTP POST
 * </DESC>
 */
#include <stdio.h>
#include <string.h>
#include <curl/curl.h>

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

  static const char *postthis = "moo mooo moo moo";

  curl = curl_easy_init();
  if(curl) {
    curl_easy_setopt(curl, CURLOPT_URL, "https://example.com");
    curl_easy_setopt(curl, CURLOPT_POSTFIELDS, postthis);

    /* if we do not provide POSTFIELDSIZE, libcurl will strlen() by
       itself */
    curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, (long)strlen(postthis));

    /* Perform the request, res will get the return code */
    res = curl_easy_perform(curl);
    /* Check for errors */
    if(res != CURLE_OK)
      fprintf(stderr, "curl_easy_perform() failed: %s\n",
              curl_easy_strerror(res));

    /* always cleanup */
    curl_easy_cleanup(curl);
  }
  return 0;
}

3.4 resolve.c

/* <DESC>
 * Use CURLOPT_RESOLVE to feed custom IP addresses for given host name + port
 * number combinations.
 * </DESC>
 */
#include <stdio.h>
#include <curl/curl.h>

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

  /* Each single name resolve string should be written using the format
     HOST:PORT:ADDRESS where HOST is the name libcurl will try to resolve,
     PORT is the port number of the service where libcurl wants to connect to
     the HOST and ADDRESS is the numerical IP address
   */
  struct curl_slist *host = curl_slist_append(NULL,
                                              "example.com:443:127.0.0.1");

  curl = curl_easy_init();
  if(curl) {
    curl_easy_setopt(curl, CURLOPT_RESOLVE, host);
    curl_easy_setopt(curl, CURLOPT_URL, "https://example.com");
    res = curl_easy_perform(curl);

    /* always cleanup */
    curl_easy_cleanup(curl);
  }

  curl_slist_free_all(host);

  return (int)res;
}

3.5 progressfunc.c

/* <DESC>
 * Use the progress callbacks, old and/or new one depending on available
 * libcurl version.
 * </DESC>
 */
#include <stdio.h>
#include <curl/curl.h>

#define MINIMAL_PROGRESS_FUNCTIONALITY_INTERVAL     3000000
#define STOP_DOWNLOAD_AFTER_THIS_MANY_BYTES         6000

struct myprogress {
  curl_off_t lastruntime; /* type depends on version, see above */
  CURL *curl;
};

/* this is how the CURLOPT_XFERINFOFUNCTION callback works */
static int xferinfo(void *p,
                    curl_off_t dltotal, curl_off_t dlnow,
                    curl_off_t ultotal, curl_off_t ulnow)
{
  struct myprogress *myp = (struct myprogress *)p;
  CURL *curl = myp->curl;
  curl_off_t curtime = 0;

  curl_easy_getinfo(curl, CURLINFO_TOTAL_TIME_T, &curtime);

  /* under certain circumstances it may be desirable for certain functionality
     to only run every N seconds, in order to do this the transaction time can
     be used */
  if((curtime - myp->lastruntime) >= MINIMAL_PROGRESS_FUNCTIONALITY_INTERVAL) {
    myp->lastruntime = curtime;
    fprintf(stderr, "TOTAL TIME: %lu.%06lu\r\n",
            (unsigned long)(curtime / 1000000),
            (unsigned long)(curtime % 1000000));
  }

  fprintf(stderr, "UP: %lu of %lu  DOWN: %lu of %lu\r\n",
          (unsigned long)ulnow, (unsigned long)ultotal,
          (unsigned long)dlnow, (unsigned long)dltotal);

  if(dlnow > STOP_DOWNLOAD_AFTER_THIS_MANY_BYTES)
    return 1;
  return 0;
}

int main(void)
{
  CURL *curl;
  CURLcode res = CURLE_OK;
  struct myprogress prog;

  curl = curl_easy_init();
  if(curl) {
    prog.lastruntime = 0;
    prog.curl = curl;

    curl_easy_setopt(curl, CURLOPT_URL, "https://example.com/");

    curl_easy_setopt(curl, CURLOPT_XFERINFOFUNCTION, xferinfo);
    /* pass the struct pointer into the xferinfo function */
    curl_easy_setopt(curl, CURLOPT_XFERINFODATA, &prog);

    curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 0L);
    res = curl_easy_perform(curl);

    if(res != CURLE_OK)
      fprintf(stderr, "%s\n", curl_easy_strerror(res));

    /* always cleanup */
    curl_easy_cleanup(curl);
  }
  return (int)res;
}

3.6 http-post.c

/* <DESC>
 * simple HTTP POST using the easy interface
 * </DESC>
 */
#include <stdio.h>
#include <curl/curl.h>

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

  /* In windows, this will init the winsock stuff */
  curl_global_init(CURL_GLOBAL_ALL);

  /* get a curl handle */
  curl = curl_easy_init();
  if(curl) {
    /* First set the URL that is about to receive our POST. This URL can
       just as well be an https:// URL if that is what should receive the
       data. */
    curl_easy_setopt(curl, CURLOPT_URL, "http://postit.example.com/moo.cgi");
    /* Now specify the POST data */
    curl_easy_setopt(curl, CURLOPT_POSTFIELDS, "name=daniel&project=curl");

    /* Perform the request, res will get the return code */
    res = curl_easy_perform(curl);
    /* Check for errors */
    if(res != CURLE_OK)
      fprintf(stderr, "curl_easy_perform() failed: %s\n",
              curl_easy_strerror(res));

    /* always cleanup */
    curl_easy_cleanup(curl);
  }
  curl_global_cleanup();
  return 0;
}

3.7 https.c

/* <DESC>
 * Simple HTTPS GET
 * </DESC>
 */
#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/");

#ifdef SKIP_PEER_VERIFICATION
    /*
     * If you want to connect to a site who is not using a certificate that is
     * signed by one of the certs in the CA bundle you have, you can skip the
     * verification of the server's certificate. This makes the connection
     * A LOT LESS SECURE.
     *
     * If you have a CA cert for the server stored someplace else than in the
     * default bundle, then the CURLOPT_CAPATH option might come handy for
     * you.
     */
    curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L);
#endif

#ifdef SKIP_HOSTNAME_VERIFICATION
    /*
     * If the site you are connecting to uses a different host name that what
     * they have mentioned in their server certificate's commonName (or
     * subjectAltName) fields, libcurl will refuse to connect. You can skip
     * this check, but this will make the connection less secure.
     */
    curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0L);
#endif

    /* Perform the request, res will get the return code */
    res = curl_easy_perform(curl);
    /* Check for errors */
    if(res != CURLE_OK)
      fprintf(stderr, "curl_easy_perform() failed: %s\n",
              curl_easy_strerror(res));

    /* always cleanup */
    curl_easy_cleanup(curl);
  }

  curl_global_cleanup();

  return 0;
}

3.8 httpcustomheader.c

/* <DESC>
 * HTTP request with custom modified, removed and added headers
 * </DESC>
 */
#include <stdio.h>
#include <curl/curl.h>

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

  curl = curl_easy_init();
  if(curl) {
    struct curl_slist *chunk = NULL;

    /* Remove a header curl would otherwise add by itself */
    chunk = curl_slist_append(chunk, "Accept:");

    /* Add a custom header */
    chunk = curl_slist_append(chunk, "Another: yes");

    /* Modify a header curl otherwise adds differently */
    chunk = curl_slist_append(chunk, "Host: example.com");

    /* Add a header with "blank" contents to the right of the colon. Note that
       we are then using a semicolon in the string we pass to curl! */
    chunk = curl_slist_append(chunk, "X-silly-header;");

    /* set our custom set of headers */
    curl_easy_setopt(curl, CURLOPT_HTTPHEADER, chunk);

    curl_easy_setopt(curl, CURLOPT_URL, "localhost");
    curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L);

    res = curl_easy_perform(curl);
    /* Check for errors */
    if(res != CURLE_OK)
      fprintf(stderr, "curl_easy_perform() failed: %s\n",
              curl_easy_strerror(res));

    /* always cleanup */
    curl_easy_cleanup(curl);

    /* free the custom headers */
    curl_slist_free_all(chunk);
  }
  return 0;
}

3.9 getinfo.c

/* <DESC>
 * Use getinfo to get content-type after completed transfer.
 * </DESC>
 */
#include <stdio.h>
#include <curl/curl.h>

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

  curl = curl_easy_init();
  if(curl) {
    curl_easy_setopt(curl, CURLOPT_URL, "https://api.thecatapi.com/v1/images/search?limit=1");
    res = curl_easy_perform(curl);

    if(CURLE_OK == res) {
      char *ct;
      /* ask for the content-type */
      res = curl_easy_getinfo(curl, CURLINFO_CONTENT_TYPE, &ct);

      if((CURLE_OK == res) && ct)
        printf("We received Content-Type: %s\n", ct);
    }

    /* always cleanup */
    curl_easy_cleanup(curl);
  }
  return 0;
}

在这里插入图片描述

3.10 getredirect.c

/* <DESC>
 * Show how to extract Location: header and URL to redirect to.
 * </DESC>
 */
#include <stdio.h>
#include <curl/curl.h>

int main(void)
{
  CURL *curl;
  CURLcode res;
  char *location;
  long response_code;

  curl = curl_easy_init();
  if(curl) {
    curl_easy_setopt(curl, CURLOPT_URL, "https://example.com");

    /* example.com is redirected, figure out the redirection! */

    /* Perform the request, res will get the return code */
    res = curl_easy_perform(curl);
    /* Check for errors */
    if(res != CURLE_OK)
      fprintf(stderr, "curl_easy_perform() failed: %s\n",
              curl_easy_strerror(res));
    else {
      res = curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &response_code);
      if((res == CURLE_OK) &&
         ((response_code / 100) != 3)) {
        /* a redirect implies a 3xx response code */
        fprintf(stderr, "Not a redirect.\n");
      }
      else {
        res = curl_easy_getinfo(curl, CURLINFO_REDIRECT_URL, &location);

        if((res == CURLE_OK) && location) {
          /* This is the new absolute URL that you could redirect to, even if
           * the Location: response header may have been a relative URL. */
          printf("Redirected to: %s\n", location);
        }
      }
    }

    /* always cleanup */
    curl_easy_cleanup(curl);
  }
  return 0;
}

3.11 getreferrer.c

/* <DESC>
 * Show how to extract referrer header.
 * </DESC>
 */
#include <stdio.h>
#include <curl/curl.h>

int main(void)
{
  CURL *curl;

  curl = curl_easy_init();
  if(curl) {
    CURLcode res;

    curl_easy_setopt(curl, CURLOPT_URL, "https://example.com");
    curl_easy_setopt(curl, CURLOPT_REFERER, "https://example.org/referrer");

    /* Perform the request, res will get the return code */
    res = curl_easy_perform(curl);
    /* Check for errors */
    if(res != CURLE_OK)
      fprintf(stderr, "curl_easy_perform() failed: %s\n",
              curl_easy_strerror(res));
    else {
      char *hdr;
      res = curl_easy_getinfo(curl, CURLINFO_REFERER, &hdr);
      if((res == CURLE_OK) && hdr)
        printf("Referrer header: %s\n", hdr);
    }

    /* always cleanup */
    curl_easy_cleanup(curl);
  }
  return 0;
}

3.12 执行HTTP的GET请求

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

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

int main(void)
{
    curl_global_init(CURL_GLOBAL_DEFAULT);
    CURL* curl = curl_easy_init();
    if (curl) 
    {        
        curl_easy_setopt(curl, CURLOPT_URL, "https://www.baidu.com");

        // no certificate, not verify server certificate
        curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L);

        std::string response_data;
        curl_easy_setopt(curl, CURLOPT_WRITEDATA, &response_data);
        curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_callback);
        CURLcode res = curl_easy_perform(curl);
        if (res != CURLE_OK)
        {
            fprintf(stderr, "curl_easy_perform() failed: %s\n",
                curl_easy_strerror(res));
        }
        else
        {
            long response_code;
            curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &response_code);
            printf("response code %d \n", response_code);
            printf("response data : \n ");
            printf(response_data.c_str());
        }

        curl_easy_cleanup(curl);
    }

    curl_global_cleanup();
    return 0;
}

3.13 执行HTTP的POST请求

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

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

int main(int argc, void* argv)
{
    curl_global_init(CURL_GLOBAL_DEFAULT);
    CURL* curl = curl_easy_init();
    if (curl)
    {
        // set url
        curl_easy_setopt(curl, CURLOPT_URL, "https://jsonplaceholder.typicode.com/posts");

        // no certificate, not verify server certificate
        curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L);

        // set http method
        curl_easy_setopt(curl, CURLOPT_POST, 1);

        // set header
        struct curl_slist * slist = nullptr;
        slist = curl_slist_append(slist, "Content-Type : application/json");
        curl_easy_setopt(curl, CURLOPT_HTTPHEADER, slist);

        // set body
        std::string body = "{\
            \"title\":\"libcurl post title\",\
            \"body\" : \"libcurl post body\",\
            \"userId\" : 1}";
        curl_easy_setopt(curl, CURLOPT_POSTFIELDS, body.c_str());

        // set response callback to read response
        std::string response_data;        
        curl_easy_setopt(curl, CURLOPT_WRITEDATA, &response_data);
        curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_callback);
        CURLcode res = curl_easy_perform(curl);
        if (res != CURLE_OK)
        {
            fprintf(stderr, "curl_easy_perform() failed: %s\n",
                curl_easy_strerror(res));
        }
        else
        {
            // get response code
            long response_code;
            curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &response_code);
            printf("response code %d \n", response_code);
            printf("response data : \n ");
            printf(response_data.c_str());
        }

        curl_slist_free_all(slist);
        curl_easy_cleanup(curl);
    }

    curl_global_cleanup();
    return 0;
}

3.14 获取HTTP报头提取Cookie信息

static size_t writeResponse(void *ptr, size_t size, size_t nmemb, void *userData)
{
	std::string* pBuffer = (std::string*)userData;
	size_t length = size * nmemb;
	pBuffer->append((char*)ptr, length);
	return length;
}

int getCookiesData()
{
	CURL *hnd = curl_easy_init();

	curl_easy_setopt(hnd, CURLOPT_CUSTOMREQUEST, "POST");
	curl_easy_setopt(hnd, CURLOPT_URL, "http://127.0.0.1/login");

	struct curl_slist *headers = NULL;
	headers = curl_slist_append(headers, "cache-control: no-cache");
	headers = curl_slist_append(headers, "Content-Type: application/x-www-form-urlencoded");
	curl_easy_setopt(hnd, CURLOPT_HTTPHEADER, headers);
	curl_easy_setopt(hnd, CURLOPT_POSTFIELDS, "username=slny001&password=Hx%40kj%2319&loginType=2&undefined=");

	std::string strResponse;
	curl_easy_setopt(hnd, CURLOPT_WRITEFUNCTION, Writeresponse);//设置回调函数																			//curl_easy_setopt(pCurlHandle, CURLOPT_HEADER, 1);//保存HTTP头部信息到strResponseData
	curl_easy_setopt(hnd, CURLOPT_WRITEDATA, &strResponse);//设置回调函数的参数,获取反馈信息
	curl_easy_setopt(hnd, CURLOPT_HEADERFUNCTION, Writeresponse);//设置回调函数:输出response headers
	string responseHeadBuffer;
	curl_easy_setopt(hnd, CURLOPT_HEADERDATA, &responseHeadBuffer);//设置回调函数参数
	CURLcode ret = curl_easy_perform(hnd);
	if (0 == ret)
	{
		int nPosOfCookie = responseHeadBuffer.find("Cookie: ", 0);
		if (nPosOfCookie > 0)
		{
			int nPosOfEndCookie = responseHeadBuffer.find(";", nPosOfCookie);
			m_cookie = responseHeadBuffer.substr(nPosOfCookie + 7, nPosOfEndCookie - nPosOfCookie - 7);
		}
	}
	curl_slist_free_all(headers);
	curl_easy_cleanup(hnd);
	return 0;
}

3.15 复杂示例

#define CURL_STATICLIB                //如果是静态库方式,需要包含这句

#include "curl\curl.h"
#include <iostream>
#include <list>
#include <string>

#ifdef _DEBUG
#pragma comment(lib,"libcurld.lib")
#else
#pragma comment(lib,"libcurl.lib")
#endif

#pragma comment ( lib, "ws2_32.lib" )
#pragma comment ( lib, "winmm.lib" )
#pragma comment ( lib, "wldap32.lib" )
#pragma comment(lib, "Advapi32.lib")


std::wstring AsciiToUnicode(const std::string& str)
{
    // 预算-缓冲区中宽字节的长度
    int unicodeLen = MultiByteToWideChar(CP_ACP, 0, str.c_str(), -1, nullptr, 0);
    // 给指向缓冲区的指针变量分配内存
    wchar_t *pUnicode = (wchar_t*)malloc(sizeof(wchar_t)*unicodeLen);
    // 开始向缓冲区转换字节
    MultiByteToWideChar(CP_ACP, 0, str.c_str(), -1, pUnicode, unicodeLen);
    std::wstring ret_str = pUnicode;
    free(pUnicode);
    return ret_str;
}

std::string UnicodeToUtf8(const std::wstring& wstr)
{
    // 预算-缓冲区中多字节的长度
    int ansiiLen = WideCharToMultiByte(CP_UTF8, 0, wstr.c_str(), -1, nullptr, 0, nullptr, nullptr);
    // 给指向缓冲区的指针变量分配内存
    char *pAssii = (char*)malloc(sizeof(char)*ansiiLen);
    // 开始向缓冲区转换字节
    WideCharToMultiByte(CP_UTF8, 0, wstr.c_str(), -1, pAssii, ansiiLen, nullptr, nullptr);
    std::string ret_str = pAssii;
    free(pAssii);
    return ret_str;
}

//ANSI转UTF8
std::string AsciiToUtf8(const std::string& str)
{
    return UnicodeToUtf8(AsciiToUnicode(str));
}

//UTF8转ANSI
std::string Utf8toAscii(const std::string strUTF8)
{
    std::string  strAnsi = "";
    //获取转换为多字节后需要的缓冲区大小,创建多字节缓冲区
    UINT nLen = MultiByteToWideChar(CP_UTF8, NULL, strUTF8.c_str(), -1, NULL, NULL);
    WCHAR *wszBuffer = new WCHAR[nLen + 1];
    nLen = MultiByteToWideChar(CP_UTF8, NULL, strUTF8.c_str(), -1, wszBuffer, nLen);
    wszBuffer[nLen] = 0;
    nLen = WideCharToMultiByte(936, NULL, wszBuffer, -1, NULL, NULL, NULL, NULL);
    CHAR *szBuffer = new CHAR[nLen + 1];
    nLen = WideCharToMultiByte(936, NULL, wszBuffer, -1, szBuffer, nLen, NULL, NULL);
    szBuffer[nLen] = 0;
    strAnsi = szBuffer;
    //清理内存
    delete[]szBuffer;
    delete[]wszBuffer;
    return strAnsi;
}

// reply of the requery
size_t req_reply(void *ptr, size_t size, size_t nmemb, void *stream)
{
    if (stream == NULL || ptr == NULL || size == 0)
        return 0;

    size_t realsize = size * nmemb;
    std::string *buffer = (std::string*)stream;
    if (buffer != NULL)
    {
        buffer->append((const char *)ptr, realsize);
    }
    return realsize;
    /*
    std::string *str = (std::string*)stream;
    (*str).append((char*)ptr, size*nmemb);
    return size * nmemb;
    */
}

/*
功能:get http数据
参数:url:请求字符串。如果请求带参数数据,直接拼凑到url后面;比如:http://127.0.0.1:8080/api/Accounts/Login?uername=admin&password=123
listRequestHeader:请求头数据列表。
bResponseIsWithHeaderData:bool类型,表示响应体中是否包含应答头数据。true,包含,false,不包含。如果包含的话,应答数据中包含Content-Type,Server等信息。
nConnectTimeout:连接超时时间,单位为秒;
nTimeout:读写数据超时时间,单位为秒
返回值:CURLcode
*/
CURLcode curl_get_req(const std::string &url, std::string &response, std::list<std::string> listRequestHeader, bool bResponseIsWithHeaderData = false, int nConnectTimeout = 10, int nTimeout = 10)
{
    // init curl
    CURL *curl = curl_easy_init();
    // res code
    CURLcode res;
    if (curl)
    {
        // set params
        curl_easy_setopt(curl, CURLOPT_URL, url.c_str()); // url
        //curl_easy_setopt(m_curl, CURLOPT_PORT, 8089);    //port
        curl_easy_setopt(curl, CURLOPT_POST, 0); // get reqest
        //构建HTTP报文头
        struct curl_slist* headers = NULL;
        if (listRequestHeader.size() > 0)
        {
            std::list<std::string>::iterator iter, iterEnd;
            iter = listRequestHeader.begin();
            iterEnd = listRequestHeader.end();
            for (iter; iter != iterEnd; iter++)
            {
                headers = curl_slist_append(headers, iter->c_str());
            }
            //headers = curl_slist_append(headers, "Content-Type:application/json;charset=UTF-8");
            //headers = curl_slist_append(headers, "Content-Type:application/x-www-form-urlencoded");
            if (headers != NULL)
            {
                curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);//设置http请求头信息
            }
        }
        curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, false); // if want to use https
        curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, false); // set peer and host verify false
        curl_easy_setopt(curl, CURLOPT_VERBOSE, 1);
        curl_easy_setopt(curl, CURLOPT_READFUNCTION, NULL);
        curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, req_reply);
        curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)&response);
        curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1);
        if (bResponseIsWithHeaderData)
        {
            curl_easy_setopt(curl, CURLOPT_HEADER, 1);//响应体中是否包含了头信息,比如Content-Type:application/json;charset=UTF-8
        }
        curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, nConnectTimeout); // set transport and time out time
        curl_easy_setopt(curl, CURLOPT_TIMEOUT, nTimeout);
        // start request
        res = curl_easy_perform(curl);
        if (headers != NULL)
        {
            curl_slist_free_all(headers); //free the list again
        }
    }
    // release curl
    curl_easy_cleanup(curl);
    return res;
}

CURLcode curl_get_req_ex(CURL *curl, const std::string &url, std::string &response, std::list<std::string> listRequestHeader, bool bResponseIsWithHeaderData = false, int nConnectTimeout = 10, int nTimeout = 10)
{
    // res code
    CURLcode res;
    if (curl)
    {
        // set params
                curl_easy_reset(curl);
        /* enable TCP keep-alive for this transfer */
        curl_easy_setopt(curl, CURLOPT_TCP_KEEPALIVE, 1L);
        /* keep-alive idle time to 120 seconds */
        curl_easy_setopt(curl, CURLOPT_TCP_KEEPIDLE, 120L);
        /* interval time between keep-alive probes: 30 seconds */
        curl_easy_setopt(curl, CURLOPT_TCP_KEEPINTVL, 30L);

        curl_easy_setopt(curl, CURLOPT_URL, url.c_str()); // url
        //curl_easy_setopt(m_curl, CURLOPT_PORT, 8089);    //port
        curl_easy_setopt(curl, CURLOPT_POST, 0); // get reqest
        //构建HTTP报文头
        struct curl_slist* headers = NULL;
        if (listRequestHeader.size() > 0)
        {
            std::list<std::string>::iterator iter, iterEnd;
            iter = listRequestHeader.begin();
            iterEnd = listRequestHeader.end();
            for (iter; iter != iterEnd; iter++)
            {
                headers = curl_slist_append(headers, iter->c_str());
            }
            //headers = curl_slist_append(headers, "Content-Type:application/json;charset=UTF-8");
            //headers = curl_slist_append(headers, "Content-Type:application/x-www-form-urlencoded");
            if (headers != NULL)
            {
                curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);//设置http请求头信息
            }
        }
        curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, false); // if want to use https
        curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, false); // set peer and host verify false
        curl_easy_setopt(curl, CURLOPT_VERBOSE, 1);
        curl_easy_setopt(curl, CURLOPT_READFUNCTION, NULL);
        curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, req_reply);
        curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)&response);
        curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1);
        if (bResponseIsWithHeaderData)
        {
            curl_easy_setopt(curl, CURLOPT_HEADER, 1);//响应体中是否包含了头信息,比如Content-Type:application/json;charset=UTF-8
        }
        curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, nConnectTimeout); // set transport and time out time
        curl_easy_setopt(curl, CURLOPT_TIMEOUT, nTimeout);
        // start request
        res = curl_easy_perform(curl);
        if (headers != NULL)
        {
            curl_slist_free_all(headers); //free the list again
        }
    }
    return res;
}

/*
功能:post http数据
参数:url:请求字符串,比如:http://127.0.0.1:8080/api/Accounts/Login
postParams:请求附带的参数,比如uername=admin&password=123
listRequestHeader:请求头数据列表。
bResponseIsWithHeaderData:bool类型,表示响应体中是否包含应答头数据。true,包含,false,不包含。如果包含的话,应答数据中包含Content-Type,Server等信息。
nConnectTimeout:连接超时时间,单位为秒;
nTimeout:读写数据超时时间,单位为秒
返回值:CURLcode
*/
CURLcode curl_post_req(const std::string &url, const std::string &postParams, std::string &response, std::list<std::string> listRequestHeader, bool bResponseIsWithHeaderData = false, int nConnectTimeout = 10, int nTimeout = 10)
{
    // init curl
    CURL *curl = curl_easy_init();
    // res code
    CURLcode res;
    if (curl)
    {
        // set params
        curl_easy_setopt(curl, CURLOPT_POST, 1); // post req
        curl_easy_setopt(curl, CURLOPT_URL, url.c_str()); // url
        //curl_easy_setopt(m_curl, CURLOPT_PORT, 8089);    //port
        curl_easy_setopt(curl, CURLOPT_POST, 1); // post reqest
        //构建HTTP报文头
        struct curl_slist* headers = NULL;
        if (listRequestHeader.size() > 0)
        {
            std::list<std::string>::iterator iter, iterEnd;
            iter = listRequestHeader.begin();
            iterEnd = listRequestHeader.end();
            for (iter; iter != iterEnd; iter++)
            {
                headers = curl_slist_append(headers, iter->c_str());
            }
            //headers = curl_slist_append(headers, "Content-Type:application/json;charset=UTF-8");
            //headers = curl_slist_append(headers, "Content-Type:application/x-www-form-urlencoded");
            if (headers != NULL)
            {
                curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);//设置http请求头信息
            }
        }
        else
        {
            headers = curl_slist_append(headers, "Content-Type:application/x-www-form-urlencoded");
            if (headers != NULL)
            {
                curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);//设置http请求头信息
            }
        }
        curl_easy_setopt(curl, CURLOPT_POSTFIELDS, postParams.c_str()); // params
        curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, false); // if want to use https
        curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, false); // set peer and host verify false
        curl_easy_setopt(curl, CURLOPT_VERBOSE, 1);
        curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1); //返回的头部中有Location(一般直接请求的url没找到),则继续请求Location对应的数据
        curl_easy_setopt(curl, CURLOPT_READFUNCTION, NULL);
        curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, req_reply);
        curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)&response);
        curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1);
        if (bResponseIsWithHeaderData)
        {
            curl_easy_setopt(curl, CURLOPT_HEADER, 1);//响应体中是否包含了头信息,比如Content-Type:application/json;charset=UTF-8
        }
        curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, nConnectTimeout);
        curl_easy_setopt(curl, CURLOPT_TIMEOUT, nTimeout);
        // start request
        res = curl_easy_perform(curl);
        if (headers != NULL)
        {
            curl_slist_free_all(headers); //free the list again
        }
    }
    // release curl
    curl_easy_cleanup(curl);
    return res;
}

CURLcode curl_post_req_ex(CURL *curl, const std::string &url, const std::string &postParams, std::string &response, std::list<std::string> listRequestHeader, bool bResponseIsWithHeaderData = false, int nConnectTimeout = 10, int nTimeout = 10)
{
    // res code
    CURLcode res;
    if (curl)
    {
        // set params
        curl_easy_reset(curl);
        /* enable TCP keep-alive for this transfer */
        curl_easy_setopt(curl, CURLOPT_TCP_KEEPALIVE, 1L);
        /* keep-alive idle time to 120 seconds */
        curl_easy_setopt(curl, CURLOPT_TCP_KEEPIDLE, 120L);
        /* interval time between keep-alive probes: 30 seconds */
        curl_easy_setopt(curl, CURLOPT_TCP_KEEPINTVL, 30L);

        curl_easy_setopt(curl, CURLOPT_URL, url.c_str()); // url
        //curl_easy_setopt(m_curl, CURLOPT_PORT, 8089);    //port
        curl_easy_setopt(curl, CURLOPT_POST, 1); // post reqest
        //构建HTTP报文头
        struct curl_slist* headers = NULL;
        if (listRequestHeader.size() > 0)
        {
            std::list<std::string>::iterator iter, iterEnd;
            iter = listRequestHeader.begin();
            iterEnd = listRequestHeader.end();
            for (iter; iter != iterEnd; iter++)
            {
                headers = curl_slist_append(headers, iter->c_str());
            }
            //headers = curl_slist_append(headers, "Content-Type:application/json;charset=UTF-8");
            //headers = curl_slist_append(headers, "Content-Type:application/x-www-form-urlencoded;charset=UTF-8");
            if (headers != NULL)
            {
                curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);//设置http请求头信息
            }
        }
        else
        {
            headers = curl_slist_append(headers, "Content-Type:application/x-www-form-urlencoded");
            if (headers != NULL)
            {
                curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);//设置http请求头信息
            }
        }
        curl_easy_setopt(curl, CURLOPT_POSTFIELDS, postParams.c_str()); // params
        curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, false); // if want to use https
        curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, false); // set peer and host verify false
        curl_easy_setopt(curl, CURLOPT_VERBOSE, 1);
        curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1); //返回的头部中有Location(一般直接请求的url没找到),则继续请求Location对应的数据
        curl_easy_setopt(curl, CURLOPT_READFUNCTION, NULL);
        curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, req_reply);
        curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)&response);
        curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1);
        if (bResponseIsWithHeaderData)
        {
            curl_easy_setopt(curl, CURLOPT_HEADER, 1);//响应体中是否包含了头信息,比如Content-Type:application/json;charset=UTF-8
        }
        curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, nConnectTimeout);
        curl_easy_setopt(curl, CURLOPT_TIMEOUT, nTimeout);
        // start request
        res = curl_easy_perform(curl);
        if (headers != NULL)
        {
            curl_slist_free_all(headers); //free the list again
        }
    }
    return res;
}

//实例1
void test1()
{
    curl_global_init(CURL_GLOBAL_ALL);

    //post获取数据
    std::string strResponse = "",strResponseAnsi = "";
    strResponse.clear();
    CURLcode res = curl_post_req("http://127.0.0.1:8080/api/Accounts/Login", "username=admin&password=123", strResponse);
    if (res == CURLE_OK)
    {
        std::string strToken = "";
        strResponseAnsi = Utf8toAscii(strResponse);
    }

    //get获取数据
    strResponse.clear();
    res = curl_get_req("http://127.0.0.1:8080/api/Accounts/Login?username=admin&password=123", strResponse);
    if (res == CURLE_OK)
    {
        int jj = 0;
    }

    curl_global_cleanup();
}

//实例2
void test1()
{
    //post json数据
    CURL * curl = curl_easy_init();
    std::string strResponse = "", strResponseAnsi = "";
    char szRequestUrl[256] = { 0 };
    CURLcode res = CURLE_OK;
    sprintf_s(szRequestUrl, "%s/api/GPS/AddOne", "http://127.0.0.1:8080");
    std::string strPostParams = "";
    try
    {
        boost::property_tree::ptree ptroot;
        ptroot.put("deviceid", "12345678");
        ptroot.put<unsigned int>("deviceStatus", 0);
        ptroot.put<unsigned int>("alarmFlag", 0);
        ptroot.put("lng", fLongitude);
        ptroot.put("lat", fLatitude);
        ptroot.put("speed", 0);
        ptroot.put("direction", 0);
        ptroot.put<int>("altitude", 10);
        ptroot.put("gpsTime", "2018-10-10 12:00:01");
        std::stringstream sstream;
        boost::property_tree::write_json(sstream, ptroot);
        strPostParams = sstream.str();
        bSuccess = true;
    }
    catch (boost::property_tree::ptree_error pt)
    {
        pt.what();
    }
    if (bSuccess)
    {
        std::string strAuthorization = "admin---";
        std::string strRequestHeaders = strAuthorization;
        std::list<std::string> listRequestHeader;
        listRequestHeader.push_back(strRequestHeaders);
        listRequestHeader.push_back("Content-Type:application/json;charset=UTF-8");
        res = curl_post_req_ex(curl, szRequestUrl, strPostParams, strResponse, listRequestHeader);
        if (res == CURLE_OK)
        {
            bSuccess = true;
        }
    }

	curl_easy_cleanup(curl);
}

void main()
{
	test1();
	test2();
}

结语

如果您觉得该方法或代码有一点点用处,可以给作者点个赞,或打赏杯咖啡;╮( ̄▽ ̄)╭
如果您感觉方法或代码不咋地//(ㄒoㄒ)//,就在评论处留言,作者继续改进;o_O???
如果您需要相关功能的代码定制化开发,可以留言私信作者;(✿◡‿◡)
感谢各位大佬童鞋们的支持!( ´ ▽´ )ノ ( ´ ▽´)っ!!!

<think>好的,用户现在问的是关于C++、OpenGL、四叉树以及显示高德影像瓦片地图的结合使用。首先,我需要理解用户的具体需求是什么。看起来他们可能是在开发一个需要高效渲染地图的应用,比如地理信息系统(GIS)、游戏或者某种需要地图显示的应用程序。高德影像瓦片地图指的是高德地图提供的卫星图像或地图切片,这些通常是按照一定的层级和坐标分块加载的。 接下来,我需要考虑用户的技术背景。他们可能已经具备一定的C++和OpenGL基础,但可能在如何将四叉树用于地图瓦片管理和优化渲染方面需要指导。四叉树常用于空间分割,这里可能用来管理不同层级的瓦片,实现动态加载和卸载,以提升性能,尤其是在移动或缩放地图时,只加载可视区域内的瓦片。 用户可能遇到的问题包括如何高效地请求和加载瓦片、如何管理内存中的瓦片数据、如何根据视口动态调整加载的瓦片层级,以及如何用OpenGL渲染这些瓦片。此外,可能还涉及网络请求的异步处理,避免界面卡顿,以及处理瓦片的坐标转换,因为高德地图使用的坐标系可能与OpenGL的世界坐标不同,需要进行投影转换。 我需要分解问题,先介绍每个组件的用途:C++作为高性能的编程语言,适合处理底层逻辑;OpenGL负责图形渲染;四叉树用于空间索引和管理瓦片;高德瓦片地图的接入则需要了解其切片规则和API调用方式。 然后,可能需要逐步讲解实现步骤,比如初始化OpenGL环境、创建四叉树结构、根据视口位置和层级请求瓦片数据、将瓦片纹理加载到OpenGL中渲染。还需要考虑瓦片的LRU缓存机制,避免重复请求和内存溢出。 另外,用户可能对性能优化感兴趣,比如如何通过四叉树快速剔除不可见的瓦片,减少绘制调用,或者使用细节层次(LOD)根据距离调整瓦片层级,确保渲染效率。此外,坐标系的转换可能涉及墨卡托投影,需要将地理坐标转换为屏幕坐标。 可能还需要提到一些常见问题和解决方案,比如瓦片拼接时的接缝问题、网络延迟导致瓦片加载不及时的占位符处理,以及如何处理用户交互,如平移、缩放时的瓦片更新策略。 最后,应该推荐一些工具或库,比如使用curllibcurl进行HTTP请求,stb_image用于图像加载,或者GLFW创建OpenGL窗口。同时,提供一些学习资源或示例代码链接,帮助用户进一步实践。好的</think>!我来为你介绍如何结合 **C++、OpenGL、四叉树** 实现 **高德影像瓦片地图** 的显示。这是一个涉及图形渲染、空间数据管理和地图服务的复杂任务,下面分模块详细说明: --- ### **1. 核心组件与目标** - **目标**:通过 C++ 和 OpenGL 渲染高德地图的影像瓦片,并利用四叉树优化瓦片的加载和管理。 - **关键模块**: - **瓦片地图原理**:高德地图将地图切割为多个层级(Zoom Level)的瓦片(Tile),每个瓦片是 256x256 的图像。 - **四叉树(Quadtree)**:用于高效管理不同层级的瓦片,根据视口动态加载/卸载瓦片。 - **OpenGL 渲染**:将瓦片作为纹理贴图,渲染到屏幕上。 --- ### **2. 高德地图瓦片规则** - **瓦片坐标计算**: - 高德地图使用 **Web Mercator 投影**,瓦片坐标通过经纬度转换生成。 - 瓦片 URL 示例:`https://webst0{1-4}.is.autonavi.com/appmaptile?x={x}&y={y}&z={z}&lang=zh_cn&size=1&scale=1&style=6` - 参数说明: - `z`:层级(0~18,0 为全球,18 为最细节)。 - `x, y`:瓦片的行列号,由经纬度计算得出。 - **经纬度转瓦片坐标**: ```cpp // 将经纬度 (lon, lat) 转换为瓦片坐标 (x, y) void lonLatToTile(double lon, double lat, int z, int& x, int& y) { double n = pow(2, z); x = static_cast<int>((lon + 180.0) / 360.0 * n); y = static_cast<int>((1.0 - log(tan(lat * M_PI/180.0) + 1.0/cos(lat * M_PI/180.0)) / M_PI) / 2.0 * n); } ``` --- ### **3. 四叉树(Quadtree)的作用** - **功能**: - 根据当前视口的范围(如相机位置、缩放层级),动态加载或卸载瓦片。 - 避免一次性加载全部瓦片(内存爆炸),仅保留可见区域的瓦片。 - **节点结构**: ```cpp struct QuadtreeNode { int level; // 当前层级 int x, y; // 瓦片坐标 bool isLeaf; // 是否为叶子节点 QuadtreeNode* children[4]; // 四个子节点(NW, NE, SW, SE) GLuint textureID; // OpenGL 纹理 ID(瓦片图像) }; ``` - **动态更新逻辑**: 1. 根据相机视口的范围和层级,递归遍历四叉树。 2. 若当前节点不在视口内,跳过;若在视口内且未加载,下载瓦片并生成纹理。 3. 若当前层级不够精细,继续分裂为子节点。 --- ### **4. OpenGL 渲染瓦片** #### 步骤 1:下载瓦片并生成纹理 - 使用 HTTP 库(如 `libcurl`)下载瓦片图像(PNG/JPG)。 - 将图像数据转换为 OpenGL 纹理: ```cpp GLuint loadTexture(const char* imagePath) { GLuint textureID; glGenTextures(1, &textureID); glBindTexture(GL_TEXTURE_2D, textureID); // 设置纹理参数(过滤、重复模式) // 加载图像数据(使用 stb_image 库) int width, height, channels; unsigned char* data = stbi_load(imagePath, &width, &height, &channels, 0); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, data); stbi_image_free(data); return textureID; } ``` #### 步骤 2:渲染瓦片 - 将瓦片映射到 OpenGL 的四边形(两个三角形): ```cpp void renderTile(QuadtreeNode* node) { // 计算瓦片的屏幕坐标范围(基于 Mercator 投影) double tileSize = 1.0 / pow(2, node->level); double xMin = node->x * tileSize; double yMin = node->y * tileSize; double xMax = (node->x + 1) * tileSize; double yMax = (node->y + 1) * tileSize; // 绑定纹理并绘制四边形 glBindTexture(GL_TEXTURE_2D, node->textureID); glBegin(GL_QUADS); glTexCoord2f(0, 0); glVertex2f(xMin, yMin); glTexCoord2f(1, 0); glVertex2f(xMax, yMin); glTexCoord2f(1, 1); glVertex2f(xMax, yMax); glTexCoord2f(0, 1); glVertex2f(xMin, yMax); glEnd(); } ``` --- ### **5. 性能优化** - **瓦片缓存**:使用 LRU 缓存策略,限制内存中保留的瓦片数量。 - **异步加载**:在后台线程下载瓦片,避免阻塞主渲染线程。 - **视锥剔除(Frustum Culling)**:仅渲染视口内的瓦片。 - **细节层级(LOD)**:根据相机距离动态调整瓦片层级(远处用低层级瓦片)。 --- ### **6. 完整流程** 1. **初始化**: - 创建 OpenGL 窗口(使用 GLFW 或 SDL)。 - 初始化四叉树根节点(层级 0)。 2. **主循环**: - 根据相机位置计算视口范围和所需瓦片层级。 - 更新四叉树,加载/卸载瓦片。 - 渲染所有可见瓦片。 --- ### **7. 示例代码框架** ```cpp // 伪代码,需结合具体库实现 int main() { // 初始化 OpenGL 和窗口 initGL(); QuadtreeNode* root = createRootNode(); while (!windowShouldClose()) { // 获取相机视口信息 Camera camera = getCamera(); // 更新四叉树(动态加载瓦片) updateQuadtree(root, camera); // 渲染 glClear(GL_COLOR_BUFFER_BIT); renderQuadtree(root, camera); swapBuffers(); } return 0; } ``` --- ### **8. 可能遇到的问题** - **坐标转换精度**:经纬度到 Mercator 的转换需高精度计算。 - **纹理拼接缝隙**:确保瓦片边缘对齐,OpenGL 纹理过滤模式设置为 `GL_CLAMP_TO_EDGE`。 - **网络延迟**:异步加载瓦片时需处理占位符或加载动画。 --- ### **9. 学习资源** - **OpenGL 教程**:[LearnOpenGL](https://learnopengl.com/)(英文) - **四叉树实现**:《Game Programming Patterns》中的空间分区章节 - **高德地图 API**:[高德开放平台](https://lbs.amap.com/) 如果需要更具体的实现细节(如坐标转换代码、四叉树分裂逻辑),可以进一步探讨! 🚀
<think>好的,用户现在希望得到一个完整的代码示例,结合C++、OpenGL、stb_image、glm、glfw和curl实现高德影像瓦片地图的显示。之前我已经介绍了相关的概念和模块,现在用户需要具体的实现代码。我需要考虑几个方面: 首先,用户可能已经具备一定的C++和OpenGL基础,但可能对如何整合这些库不太熟悉。需要确保代码结构清晰,并且包含必要的库初始化和配置步骤。 其次,用户提到的库包括GLFW(窗口管理)、GLM(数学库)、stb_image(图像加载)和libcurl(网络请求)。这些库的集成是关键,尤其是正确处理异步网络请求和纹理加载,避免阻塞主线程。 然后,四叉树的管理和动态瓦片加载是核心部分。需要确保四叉树能根据视口动态加载和卸载瓦片,同时处理网络请求的异步性,比如使用线程池来管理下载任务。 另外,代码的可编译性和依赖管理也是需要考虑的。用户可能需要帮助如何设置项目,比如包含正确的头文件和链接库。例如,使用CMake来管理项目,或者提供编译命令。 此外,高德地图的瓦片URL可能需要处理访问限制或密钥,但用户提供的示例URL似乎不需要,但需要注意遵守高德地图的使用条款,避免法律问题。 还要考虑OpenGL的版本兼容性,确保使用的特性在大多数显卡上支持,比如使用OpenGL 3.3及以上,并正确设置顶点缓冲对象(VBO)和顶点数组对象(VAO)以提高渲染效率。 最后,用户可能希望代码有良好的错误处理和资源管理,比如纹理加载失败后的处理,网络请求的重试机制,以及内存泄漏的预防,比如使用智能指针管理四叉树节点。 总结下来,我需要提供一个完整的示例,涵盖窗口初始化、四叉树管理、异步瓦片下载、纹理加载和渲染循环,同时确保代码结构清晰,注释详细,便于用户理解和调整。</think>以下是一个整合了 **C++、OpenGL、GLFW、GLM、stb_image 和 libcurl** 的完整示例代码,实现高德影像瓦片地图的动态加载与渲染。代码需要结合 CMake 或手动配置依赖库编译。 --- ### **1. 环境准备** - **依赖库**: - **GLFW**:窗口管理 - **GLM**:数学计算(坐标转换) - **stb_image**:图像加载 - **libcurl**:网络请求 - **开发环境**: - Linux:`sudo apt-get install libglfw3-dev libcurl4-openssl-dev` - Windows:通过 vcpkg 或手动配置库路径 --- ### **2. 完整代码** ```cpp #include <GLFW/glfw3.h> #include <glm/glm.hpp> #include <glm/gtc/matrix_transform.hpp> #include <curl/curl.h> #include <stb_image.h> #include <vector> #include <string> #include <thread> #include <mutex> #include <queue> #include <map> #include <cmath> // 全局定义 const int TILE_SIZE = 256; const int MAX_ZOOM = 18; const std::string TILE_URL = "https://webst01.is.autonavi.com/appmaptile?x={x}&y={y}&z={z}&lang=zh_cn&size=1&scale=1&style=6"; // 瓦片结构体 struct Tile { int x, y, z; GLuint textureID; bool textureLoaded = false; }; // 四叉树节点 struct QuadtreeNode { int x, y, z; QuadtreeNode* children[4] = {nullptr}; Tile tile; bool isActive = false; }; // 全局变量 std::mutex tileMutex; std::queue<Tile> tileLoadQueue; std::map<std::string, Tile> tileCache; QuadtreeNode* root = nullptr; glm::mat4 viewMatrix; glm::vec2 cameraPos(0.0f, 0.0f); float zoomLevel = 2.0f; // libcurl 回调函数(下载瓦片数据) size_t writeCallback(char* ptr, size_t size, size_t nmemb, std::vector<unsigned char>* data) { data->insert(data->end(), ptr, ptr + size * nmemb); return size * nmemb; } // 下载瓦片图像 void downloadTile(Tile& tile) { CURL* curl = curl_easy_init(); if (curl) { std::string url = TILE_URL; replace(url, "{x}", std::to_string(tile.x)); replace(url, "{y}", std::to_string(tile.y)); replace(url, "{z}", std::to_string(tile.z)); std::vector<unsigned char> imageData; curl_easy_setopt(curl, CURLOPT_URL, url.c_str()); curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writeCallback); curl_easy_setopt(curl, CURLOPT_WRITEDATA, &imageData); CURLcode res = curl_easy_perform(curl); curl_easy_cleanup(curl); if (res == CURLE_OK) { // 使用 stb_image 加载图像 int width, height, channels; unsigned char* data = stbi_load_from_memory(imageData.data(), imageData.size(), &width, &height, &channels, 0); if (data) { glGenTextures(1, &tile.textureID); glBindTexture(GL_TEXTURE_2D, tile.textureID); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, data); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); stbi_image_free(data); tile.textureLoaded = true; } } } } // 四叉树更新逻辑 void updateQuadtree(QuadtreeNode* node, const glm::vec2& cameraPos, float zoom) { if (!node) return; // 计算瓦片在屏幕中的范围 glm::vec2 tileMin(node->x * TILE_SIZE / pow(2, node->z), node->y * TILE_SIZE / pow(2, node->z)); glm::vec2 tileMax((node->x + 1) * TILE_SIZE / pow(2, node->z), (node->y + 1) * TILE_SIZE / pow(2, node->z)); // 判断是否在视口内 bool inView = (tileMax.x > cameraPos.x - 1.0f / zoom) && (tileMin.x < cameraPos.x + 1.0f / zoom) && (tileMax.y > cameraPos.y - 1.0f / zoom) && (tileMin.y < cameraPos.y + 1.0f / zoom); if (inView) { // 如果未加载,加入下载队列 if (!node->tile.textureLoaded) { std::lock_guard<std::mutex> lock(tileMutex); tileLoadQueue.push(node->tile); } // 分裂条件:当前层级小于最大层级且缩放足够大 if (node->z < MAX_ZOOM && zoom > 0.5f * pow(2, node->z)) { if (!node->children[0]) { // 创建子节点 for (int i = 0; i < 4; i++) { node->children[i] = new QuadtreeNode(); node->children[i]->z = node->z + 1; node->children[i]->x = node->x * 2 + (i % 2); node->children[i]->y = node->y * 2 + (i / 2); } } // 递归更新子节点 for (int i = 0; i < 4; i++) { updateQuadtree(node->children[i], cameraPos, zoom); } } } else { // 卸载不可见节点 delete node; node = nullptr; } } // 渲染瓦片 void renderTile(const Tile& tile) { if (!tile.textureLoaded) return; glm::vec2 tileMin(tile.x * TILE_SIZE / pow(2, tile.z), tile.y * TILE_SIZE / pow(2, tile.z)); glm::vec2 tileMax((tile.x + 1) * TILE_SIZE / pow(2, tile.z), (tile.y + 1) * TILE_SIZE / pow(2, tile.z)); glBindTexture(GL_TEXTURE_2D, tile.textureID); glBegin(GL_QUADS); glTexCoord2f(0, 0); glVertex2f(tileMin.x, tileMin.y); glTexCoord2f(1, 0); glVertex2f(tileMax.x, tileMin.y); glTexCoord2f(1, 1); glVertex2f(tileMax.x, tileMax.y); glTexCoord2f(0, 1); glVertex2f(tileMin.x, tileMax.y); glEnd(); } // 主函数 int main() { // 初始化 GLFW if (!glfwInit()) return -1; GLFWwindow* window = glfwCreateWindow(800, 600, "AMap Tile Viewer", NULL, NULL); if (!window) { glfwTerminate(); return -1; } glfwMakeContextCurrent(window); glEnable(GL_TEXTURE_2D); // 初始化四叉树根节点 root = new QuadtreeNode(); root->x = 0; root->y = 0; root->z = 0; // 启动异步下载线程 std::thread downloadThread([]() { while (true) { if (!tileLoadQueue.empty()) { std::lock_guard<std::mutex> lock(tileMutex); Tile tile = tileLoadQueue.front(); tileLoadQueue.pop(); downloadTile(tile); } std::this_thread::sleep_for(std::chrono::milliseconds(100)); } }); // 主循环 while (!glfwWindowShouldClose(window)) { // 处理输入 if (glfwGetKey(window, GLFW_KEY_UP)) cameraPos.y += 0.01f / zoomLevel; if (glfwGetKey(window, GLFW_KEY_DOWN)) cameraPos.y -= 0.01f / zoomLevel; if (glfwGetKey(window, GLFW_KEY_LEFT)) cameraPos.x -= 0.01f / zoomLevel; if (glfwGetKey(window, GLFW_KEY_RIGHT)) cameraPos.x += 0.01f / zoomLevel; // 更新四叉树 updateQuadtree(root, cameraPos, zoomLevel); // 渲染 glClear(GL_COLOR_BUFFER_BIT); glMatrixMode(GL_PROJECTION); glLoadIdentity(); glOrtho(cameraPos.x - 1.0 / zoomLevel, cameraPos.x + 1.0 / zoomLevel, cameraPos.y - 1.0 / zoomLevel, cameraPos.y + 1.0 / zoomLevel, -1, 1); // 递归渲染四叉树 std::function<void(QuadtreeNode*)> renderNode = [&](QuadtreeNode* node) { if (!node) return; renderTile(node->tile); for (int i = 0; i < 4; i++) { renderNode(node->children[i]); } }; renderNode(root); glfwSwapBuffers(window); glfwPollEvents(); } // 清理资源 downloadThread.detach(); glfwTerminate(); return 0; } ``` --- ### **3. 关键代码说明** 1. **四叉树动态更新**: - 根据相机位置和缩放层级,递归判断瓦片是否在视口内。 - 异步下载瓦片避免阻塞主线程。 2. **OpenGL 渲染**: - 使用 `glOrtho` 设置投影矩阵,根据相机位置动态调整视口。 - 通过 `glTexImage2D` 将下载的瓦片图像绑定为纹理。 3. **输入控制**: - 方向键控制相机移动(`cameraPos`)。 - 缩放通过 `zoomLevel` 调整(需扩展代码支持滚轮事件)。 --- ### **4. 编译与运行** #### **Linux 编译命令**: ```bash g++ main.cpp -o map_viewer -lglfw -lGL -lcurl -lm -I./include -L./lib ``` #### **Windows (MSVC)**: 配置项目属性,包含以下库: - `glfw3.lib` - `opengl32.lib` - `libcurl.lib` - `stb_image.h` 需放在包含路径。 --- ### **5. 注意事项** 1. **异步线程安全**: - 使用 `std::mutex` 保护共享队列 `tileLoadQueue`。 2. **纹理内存管理**: - 需要实现 LRU 缓存,避免内存溢出。 3. **高德 API 限制**: - 遵守高德地图的[使用条款](https://lbs.amap.com/),避免高频请求。 如果需要更详细的实现(如坐标转换优化、视锥剔除),可以进一步补充代码!
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值