目录
curl_global_init(CURL_GLOBAL_ALL);
FindCURL 模块
FindCURL模块是CMake中的一个模块,用于查找和配置libcurl库,这是一个用于传输文件和数据的常用C语言库。使用该模块可以在CMake中轻松地配置和链接Curl库,而无需手动设置所有必要的编译器标志和链接器标志。
使用该模块的示例:
find_package(CURL REQUIRED)
include_directories(${CURL_INCLUDE_DIR})
target_link_libraries(my_executable ${CURL_LIBRARIES})
在这里,find_package
命令将查找Curl库并设置CURL_INCLUDE_DIR
和CURL_LIBRARIES
变量,其中前者是包含文件路径,后者是库文件路径。然后,可以使用include_directories
命令将包含路径添加到可执行文件的编译器标志中,并使用target_link_libraries
命令将库文件链接到可执行文件中。
libcurl库
libcurl是一个使用C语言编写的、支持多种协议的开源网络库,可以用来进行HTTP、FTP、SMTP等协议的数据传输。它提供了简单的API接口,方便地进行HTTP请求和数据的发送与接收。libcurl被广泛应用于各种语言的网络编程中,例如C++、Python、Java等。在使用libcurl时,需要先安装libcurl库,并在代码中包含相应的头文件。
#include <curl/curl.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
FILE *fp;
int write_data(void *ptr, size_t size, size_t nmemb, void *stream)
{
int written = fwrite(ptr, size, nmemb, (FILE *)fp);
return written;
}
int main()
{
const char *path = "/tmp/curl-test";
const char *mode = "w";
fp = fopen(path, mode);
curl_global_init(CURL_GLOBAL_ALL);
CURLcode res;
CURL *curl = curl_easy_init();
curl_easy_setopt(curl, CURLOPT_URL, "http://www.linux-ren.org");
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_data);
curl_easy_setopt(curl, CURLOPT_VERBOSE, 1);
res = curl_easy_perform(curl);
curl_easy_cleanup(curl);
}
#include <curl/curl.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
curl/curl.h
:这是libcurl的主头文件,定义了libcurl的API函数和数据类型。stdio.h
:这是标准输入输出函数库的头文件,其中包含了输入输出函数所需的函数原型、宏和数据类型等定义。stdlib.h
:这是C标准库中的头文件,其中包含了许多通用工具函数的函数原型、宏和数据类型等定义。unistd.h
:这是Unix标准中定义的头文件,其中包含了一些Unix系统调用函数的函数原型和常量定义。在这个例子中,它用来引用文件操作函数中的一些常量定义。
FILE *fp;
FILE *fp;
是在C语言中定义一个指向FILE类型的指针变量,用于指向后面打开的文件。它是一个文件指针变量,类似于指向数组的指针变量。
在上述代码中,fp
是一个指向打开的文件的指针,用于将curl下载的数据写入到该文件中。在函数write_data
中,通过fwrite函数将指针ptr指向的数据写入到文件中,参数(FILE *)fp
将指针fp强制转换为FILE类型,确保将数据写入到正确的文件中。
size_t
size_t
是一种无符号整型数据类型,通常用于表示内存大小、数组下标等非负整数。其大小是由具体平台决定,通常为4或8个字节。在C语言中,通常使用size_t
来表示数组的长度或者内存分配的大小。
int write_data(void *ptr, size_t size, size_t nmemb, void *stream)
{
int written = fwrite(ptr, size, nmemb, (FILE *)fp);
return written;
}
当你使用 curl 请求某个网站时,curl 在接收到网站响应后会将响应内容传递给你指定的回调函数(callback function)进行处理。这个回调函数就是 write_data()
,它的作用是将接收到的数据写入文件中。在回调函数的参数中,ptr
代表从服务器接收到的数据的指针,size
代表接收到的每个数据块的大小,nmemb
代表接收到的数据块的数量,stream
则是你传入 curl_easy_setopt()
函数中的第三个参数,通常情况下用于传递写入数据的文件句柄。函数的返回值则是写入文件的数据块数量。
在这里,stream 是一个指向 FILE 结构体的指针,它表示一个文件流对象。在 C 语言中,文件 I/O 操作需要使用 FILE 结构体来表示文件对象,其中包含了文件的状态信息,例如文件的位置、读写状态等等。指针变量 stream 就是用来指向这个 FILE 结构体的,通过它可以对文件进行读写操作。在这段代码中,stream 实际上指向了一个文件对象 fp,表示将写入的数据写入到这个文件中。
回调函数
回调函数(Callback function)是指由调用者(或客户端)传递给另外一个函数(或服务提供者)的可执行代码的引用,通常作为参数传递给函数。在执行过程中,函数通过调用这个可执行代码来实现一定的功能。
在上面的代码中,write_data就是一个回调函数,它被传递给了libcurl中的CURLOPT_WRITEFUNCTION选项。当libcurl需要写入数据时,就会调用这个回调函数来完成数据写入操作。
fwrite()
fwrite()
是 C 语言标准库中的一个函数,用于向文件中写入数据。它的函数原型如下:
size_t fwrite(const void *ptr, size_t size, size_t count, FILE *stream);
ptr
是一个指向要写入数据的指针,size
是每个数据项的字节数,count
是要写入的数据项数目,stream
是指向文件的指针。函数的返回值是实际写入的数据项数目。
在上面的代码中,fwrite()
函数被用于将 curl 库获取的数据写入到文件中。ptr
参数是 curl 库返回的数据指针,size
是每个数据项的字节数,nmemb
是要写入的数据项数目,stream
是指向文件的指针。函数的返回值是实际写入的数据项数目,赋值给 written
变量,最终作为 write_data()
函数的返回值。
int written = fwrite(ptr, size, nmemb, (FILE *)fp);
return written;
这行代码是将获取到的数据写入到本地文件中,通过fwrite()函数实现。其中,参数ptr是指向数据块的指针,size是每个数据块的大小,nmemb是数据块的数量,fp是文件指针。函数返回值written是fwrite()实际写入的数据块数量,它应该等于nmemb,如果返回值小于nmemb,说明写入文件失败。最后,将written作为write_data()函数的返回值。
该回调函数只使用了一个文件指针(fp)来将下载的数据写入文件中。因此,此处将void指针(stream)转换为FILE指针(fp),以便在后续的文件写入操作中使用。
const char *path = "/tmp/curl-test";
const char *mode = "w";
fp = fopen(path, mode);
const char *path
定义了要写入的文件路径为 /tmp/curl-test
。这是一个字符串常量,用 const char *
类型的指针来表示。
const char *mode
定义了打开文件的模式为 "w"
,即写入模式。这也是一个字符串常量,用 const char *
类型的指针来表示。
在下面的代码中,fopen()
函数将使用这两个参数打开一个文件,并返回一个文件指针,用于后续的文件操作。
const char *
const char *
是指向字符常量的指针类型。它指向的字符常量是不可修改的,而指针本身是可以修改的。在C语言中,字符串就是一个字符数组,以空字符'\0'作为结尾,所以用指向第一个字符的指针表示字符串是很方便的。
fopen()
fopen()
是 C 标准库中的一个函数,用于打开一个文件,并返回一个指向该文件的指针。它的原型如下:
FILE *fopen(const char *filename, const char *mode);
其中,filename
参数表示要打开的文件名,mode
参数表示打开文件的模式。在这段代码中,path
存储了文件名,mode
存储了打开文件的模式,它们被传递给 fopen()
函数来打开文件,并将返回的指针赋值给变量 fp
。
在这个例子中,文件名为 "/tmp/curl-test"
,模式为 "w"
,表示以写入模式打开文件,如果文件不存在则创建该文件,如果文件已经存在则截断文件长度为 0。
curl_global_init(CURL_GLOBAL_ALL);
curl_global_init
函数用于全局初始化 libcurl 库。在使用 libcurl 库的其他函数之前,需要调用该函数进行初始化。CURL_GLOBAL_ALL
参数表示对 SSL、文件协议以及其他协议进行初始化。
CURLcode
CURLcode
是一个枚举类型,用于表示libcurl
库函数调用的返回值。这个类型包括许多不同的返回值,例如CURLE_OK
表示操作成功完成,而CURLE_FAILED_INIT
则表示libcurl
库初始化失败。在这个例子中,CURLcode res
是一个变量,用于存储curl_easy_perform
函数的返回值。
curl_easy_init()
curl_easy_init()
是一个CURL库函数,它用于初始化CURL句柄,即创建一个新的CURL对象。这个句柄将被用来设置CURL选项,执行请求以及获取响应等。在使用CURL之前,需要先调用此函数来初始化CURL。如果初始化成功,则会返回一个指向CURL对象的指针,否则返回NULL。在本代码中,将curl_easy_init()的返回值赋值给指针变量curl。
curl_easy_setopt(curl, CURLOPT_URL, "http://www.linux-ren.org");
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_data);
curl_easy_setopt(curl, CURLOPT_VERBOSE, 1);
这三行代码用于设置 CURL 句柄(handle)的选项。
CURLOPT_URL
选项用于设置请求的 URL,这里设置为 "http://www.linux-ren.org" ,即向该URL 发送请求。CURLOPT_WRITEFUNCTION
选项用于设置写入数据的回调函数,这里设置为write_data
,即当有数据返回时,将数据写入到文件中。CURLOPT_VERBOSE
选项用于设置是否启用详细输出,这里设置为 1,即启用详细输出,将在标准错误输出中打印请求的详细信息,如请求头、响应头等。
res = curl_easy_perform(curl);
这行代码执行了实际的HTTP请求,并且将结果传回给应用程序。它将curl_easy_setopt函数中设置的所有选项应用于传输,并执行HTTP GET请求。HTTP响应的内容通过之前设置的回调函数write_data()写入到指定的文件中。执行成功时,该函数返回CURLE_OK(0),否则返回另一个错误码。
curl_easy_setopt是用于设置CURL请求选项的函数,而curl_easy_perform则是用于执行请求的函数。在设置完选项后,调用curl_easy_perform就可以发送HTTP请求并接收响应。
curl_easy_cleanup(curl);
curl_easy_cleanup
函数是用来释放使用curl_easy_init
函数初始化的CURL句柄。在使用完CURL相关操作后,需要调用该函数进行资源释放。
write_data
函数并不是由main
函数直接调用的,而是通过设置curl_easy_setopt
中的CURLOPT_WRITEFUNCTION
参数来告诉CURL库,当它下载数据时要使用write_data
函数来处理这些数据。
在执行curl_easy_perform
时,CURL库会自动调用设置的CURLOPT_WRITEFUNCTION
函数,将下载的数据传递给该函数进行处理,这就是为什么我们看不到在main
函数中显式调用write_data
函数的原因。
FIND_PACKAGE(CURL)
IF(CURL_FOUND)
INCLUDE_DIRECTORIES(${CURL_INCLUDE_DIR})
TARGET_LINK_LIBRARIES(curltest ${CURL_LIBRARY})
ELSE(CURL_FOUND)
MESSAGE(FATAL_ERROR "CURL library not found")
ENDIF(CURL_FOUND)
在这个代码片段中,首先使用FIND_PACKAGE命令查找CURL库,如果找到,则使用INCLUDE_DIRECTORIES命令添加CURL库的头文件路径,并使用TARGET_LINK_LIBRARIES命令将CURL库链接到名为“curltest”的目标可执行文件中。如果没有找到CURL库,则使用MESSAGE命令输出错误信息并终止构建。
SET(mySources viewer.c)
SET(optionalSources)
SET(optionalLibs)
FIND_PACKAGE(JPEG)
IF(JPEG_FOUND)
SET(optionalSources ${optionalSources} jpegview.c)
INCLUDE_DIRECTORIES(${JPEG_INCLUDE_DIR})
SET(optionalLibs ${optionalLibs} ${JPEG_LIBRARIES})
ADD_DEFINITIONS(-DENABLE_JPEG_SUPPORT)
ENDIF(JPEG_FOUND)
IF(PNG_FOUND)
SET(optionalSources ${optionalSources} pngview.c)
INCLUDE_DIRECTORIES(${PNG_INCLUDE_DIR})
SET(optionalLibs ${optionalLibs} ${PNG_LIBRARIES})
ADD_DEFINITIONS(-DENABLE_PNG_SUPPORT)
ENDIF(PNG_FOUND)
ADD_EXECUTABLE(viewer ${mySources} ${optionalSources})
TARGET_LINK_LIBRARIES(viewer ${optionalLibs})
SET(mySources viewer.c)
SET(optionalSources)
SET(optionalLibs)
这三行代码定义了三个变量:
mySources
:包含主要源文件viewer.c
optionalSources
:包含可选源文件(暂时为空)optionalLibs
:包含可选库(暂时为空)
SET(optionalSources ${optionalSources} jpegview.c)
这行代码的作用是将 jpegview.c
文件加入到一个名为 optionalSources
的 CMake 变量中。SET
命令的语法为:
SET(VAR [VALUE] [CACHE TYPE DOCSTRING [FORCE]])
其中,VAR
为变量名,VALUE
为变量的值,CACHE
, TYPE
, DOCSTRING
, FORCE
为可选参数。在这行代码中,VAR
为 optionalSources
,VALUE
为 ${optionalSources} jpegview.c
,即在原有的 optionalSources
变量中加入 jpegview.c
文件。${}
语法是 CMake 中的变量引用方式,表示获取变量的值。
第一个 optionalSources
是一个变量名,而第二个 optionalSources
是一个键值对中的键(key),表示要设置的变量。这个键的值是 ${optionalSources}
,表示获取变量 optionalSources
当前的值。
在 CMake 中,变量名前不加 $
符号,而变量的值在使用时需要使用 ${}
将其括起来。因此,第二个 optionalSources
是为了获取变量 optionalSources
当前的值。
ADD_DEFINITIONS(-DENABLE_JPEG_SUPPORT)
ADD_DEFINITIONS
是一个 CMake 指令,用于向编译器添加自定义的编译选项。该指令会向 CMakeLists.txt 文件中的所有子目录和源文件添加预处理器宏定义。
在这个代码段中,ADD_DEFINITIONS(-DENABLE_JPEG_SUPPORT)
添加了一个名为 ENABLE_JPEG_SUPPORT
的预处理器宏定义,以启用 JPEG 支持。在 C++ 源文件中,可以通过 #ifdef ENABLE_JPEG_SUPPORT
这样的条件编译语句来判断是否启用了 JPEG 支持。
optionalSources初始值为一个空列表,即没有任何元素。在这里,SET(optionalSources)
实际上是在声明一个变量optionalSources
并初始化为空列表。如果JPEG和PNG都存在,那么 optionalSources
最后会包含 jpegview.c
和 pngview.c
。
FIND_PATH(HELLO_INCLUDE_DIR hello.h /usr/include/hello /usr/local/include/hello)
FIND_LIBRARY(HELLO_LIBRARY NAMES hello PATH /usr/lib /usr/local/lib)
IF (HELLO_INCLUDE_DIR AND HELLO_LIBRARY)
SET(HELLO_FOUND TRUE)
ENDIF (HELLO_INCLUDE_DIR AND HELLO_LIBRARY)
IF (HELLO_FOUND)
IF (NOT HELLO_FIND_QUIETLY)
MESSAGE(STATUS "Found Hello: ${HELLO_LIBRARY}")
ENDIF (NOT HELLO_FIND_QUIETLY)
ELSE (HELLO_FOUND)
IF (HELLO_FIND_REQUIRED)
MESSAGE(FATAL_ERROR "Could not find hello library")
ENDIF (HELLO_FIND_REQUIRED)
ENDIF (HELLO_FOUND)
这段CMake代码主要是用于查找并配置hello
库,包括其头文件路径和库文件路径。具体解释如下:
FIND_PATH
宏用于查找hello.h
头文件所在路径,查找路径分别为/usr/include/hello
和/usr/local/include/hello
。找到后将路径存储在变量HELLO_INCLUDE_DIR
中。FIND_LIBRARY
宏用于查找hello
库文件所在路径,查找路径分别为/usr/lib
和/usr/local/lib
,库文件名为hello
。找到后将路径存储在变量HELLO_LIBRARY
中。IF
条件判断语句用于判断头文件路径和库文件路径是否都存在,如果都存在,则将变量HELLO_FOUND
设为TRUE
。IF
条件判断语句用于判断是否找到hello
库。如果找到,则输出信息Found Hello: ${HELLO_LIBRARY}
,否则根据HELLO_FIND_REQUIRED
变量的值来确定是输出错误信息还是跳过。ELSE
语句用于处理未找到hello
库的情况,根据HELLO_FIND_REQUIRED
变量的值来确定是输出错误信息还是跳过。
HELLO_FIND_QUIETLY
是一个可选参数,它通常在调用 FIND_LIBRARY
或 FIND_PATH
等函数时使用,用于指示这些函数在查找库或头文件时是否输出诊断信息。如果将 HELLO_FIND_QUIETLY
设置为 TRUE
,则函数会在找不到指定库或头文件时仍然返回成功,但不会输出错误信息。在示例代码中,HELLO_FIND_QUIETLY
没有显式设置,因此默认为 FALSE。
FIND_PACKAGE(HELLO)
IF(HELLO_FOUND)
ADD_EXECUTABLE(hello main.c)
INCLUDE_DIRECTORIES(${HELLO_INCLUDE_DIR})
TARGET_LINK_LIBRARIES(hello ${HELLO_LIBRARY})
ENDIF(HELLO_FOUND)
这段代码是使用CMake的命令来查找HELLO库并链接到一个可执行文件中。
FIND_PACKAGE
命令会在系统中查找名为HELLO
的包,该包应该提供了可用于构建目标的头文件和库文件。IF(HELLO_FOUND)
检查是否找到了HELLO包,如果找到了,则添加一个名为hello
的可执行文件,包括main.c
源文件,然后使用INCLUDE_DIRECTORIES
命令将HELLO_INCLUDE_DIR
目录添加到包含路径中,最后使用TARGET_LINK_LIBRARIES
将HELLO_LIBRARY
链接到hello
可执行文件中。
${HELLO_INCLUDE_DIR}
是一个变量,它表示 FIND_PACKAGE
命令查找到的 HELLO
库的头文件目录路径。${HELLO_LIBRARY}
也是一个变量,表示 FIND_PACKAGE
命令查找到的 HELLO
库的路径。在这段代码中,INCLUDE_DIRECTORIES
命令将 HELLO
库的头文件目录路径添加到项目中,TARGET_LINK_LIBRARIES
命令将 HELLO
库链接到可执行文件 hello
中。
通常情况下,${PROJECT_SOURCE_DIR}
是在顶层CMakeLists.txt文件中通过PROJECT()
命令设置的,它表示项目的根目录路径。因此,如果使用${PROJECT_SOURCE_DIR}
,则需要在项目中使用PROJECT()
命令设置项目名称。