curl使用小记——获取远端数据到内存缓冲区
Posted 人生海海 山山而川
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了curl使用小记——获取远端数据到内存缓冲区相关的知识,希望对你有一定的参考价值。
1. 概述
我在博文《curl使用小记(二)——远程下载一张图片》中介绍了如何通过Curl获取远端的文件。不过在那个例子中,将获取远端数据与写入数据的步骤混杂到一起了。在多线程的场景下,这样做可能会造成读写冲突的问题。理论上,远端访问数据是先保存到内存中,在写出到文件中。而远端访问数据到内存可以看作是读操作,是不会读冲突的。所以一个很好的策略是,一次性将数据读取到内存Buf中,再写出到文件。
2. 实现
将《curl使用小记(二)——远程下载一张图片》中的代码改进一下,具体的代码实例如下:
#include <iostream>
#include <curl/curl.h>
using namespace std;
//内存块结构体
struct MemoryStruct
{
char *memory;
size_t size;
MemoryStruct()
{
memory = (char *)malloc(1);
size = 0;
}
~MemoryStruct()
{
free(memory);
memory = NULL;
}
};
//回调函数实现:一次请求可能多次调回调函数
size_t HttpPostWriteBack(void *contents, size_t size, size_t nmemb, void *userp)
{
size_t realsize = size * nmemb;//一次回调返回的数据量
struct MemoryStruct *mem = (struct MemoryStruct *)userp;
char *ptr = (char *)realloc(mem->memory, mem->size + realsize);
if (ptr == NULL)
{
printf("not enough memory (realloc returned NULL)\\n");
return 0;
}
mem->memory = ptr;
memcpy(&(mem->memory[mem->size]), contents, realsize);
mem->size += realsize;
return realsize;//必须返回真实的数据
}
int main()
{
const char *netlink = "http://cn.bing.com/th?id=OHR.GrandsCausses_EN-CN3335882379_800x480.jpg";
const char *output = "D:/dst1.jpg";
curl_global_init(CURL_GLOBAL_ALL); //初始化全局资源
CURL *curl = curl_easy_init(); //初始化句柄
//需要的话,可以设置代理
//curl_easy_setopt(curl, CURLOPT_PROXY, "127.0.0.1:7890");
//访问网址
curl_easy_setopt(curl, CURLOPT_URL, netlink);
//设置用户代理
curl_easy_setopt(curl, CURLOPT_USERAGENT, "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (Khtml, like Gecko) Chrome/72.0.3626.121 Safari/537.36");
//获取数据
MemoryStruct chunk;
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &chunk);
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, HttpPostWriteBack);
////实现下载进度
//curl_easy_setopt(curl, CURLOPT_NOPROGRESS, false);
//curl_easy_setopt(curl, CURLOPT_PROGRESSFUNCTION, progress_callback);
//curl_easy_setopt(curl, CURLOPT_PROGRESSDATA, nullptr);
//运行
curl_easy_perform(curl);
curl_easy_cleanup(curl); //释放句柄
curl_global_cleanup(); //释放全局资源
//写出数据
FILE *fp = nullptr;
if (fopen_s(&fp, output, "wb") != 0)
{
curl_easy_cleanup(curl);
return 0;
}
fwrite(chunk.memory, chunk.size, 1, fp);
fclose(fp);
return 1;
}
这段代码其中一个关键改进在于,通过自定义结构体MemoryStruct,实现了一个类似于动态数组的设计。由于远端访问文件的数据量在一开始并不能确定,所以需要先访问一部分,然后将容器扩容,再访问一部分,再扩容。这个申请内存的扩容操作是通过C的realloc()函数来实现的。这个结构体MemoryStruct还利用了C++的RAII机制做内存管理。
另外一个关键就是CURLOPT_WRITEDATA于CURLOPT_WRITEFUNCTION的配合使用了。CURLOPT_WRITEFUNCTION用来设置回调函数,CURLOPT_WRITEDATA用来设置回调函数的出参,这个其实是C的编程思维,万物皆指针,所有的操作都被抽象成同一个函数接口,其实不是同一个东西。
3. 参考
tensorflow小记2
with tf.device("gpu:1"): 该语句用来指派特定的CPU或GPU执行操作;
变量是为了维护图执行过程中的状态信息;
当训练模型时,用变量来存储和更新参数,变量包含张量存放于内存的缓冲区,建模时需要被明确的初始化,模型训练后它们必须被存储到磁盘中;模型训练和分析时变量可被加载;
变量的初始化必须在模型的其他操作之前先明确的完成,最简单的方法就是添加一个给所有变量初始化的操作,并在使用模型之前首先运行那个操作;从模型中恢复变量时,不需要先对它们做初始化;
tf.initialize_all_variables() 初始化所有变量;也可以传入一组变量进行初始化;
initialized_value() 初始化一个变量;
变量作用域工作:
当调用tf.get_variable时,tf.get_variable_scope().reuse==False时,作用域是为创建新变量设置的;创建变量的全程将会由当前变量作用域+所提供的名字组成并检查;
当tf.get_variable_scope().reuse==True时,作用域就是为重置变量所设置,此时调用会搜索一个已经存在的变量,它的全称和当前变量的作用域名+所提供的名字相等;
在图(graph)中定义的操作是描绘的表达式的一部分,调用run执行表达式之前,并不会真正执行;
使用feed机制可以临时替代图中的任意操作中的tensor可以对图中任何操作提交补丁,直接插入一个tensor,但要使用tf.placeholder()为这些操作创建占位符;
为了减少过拟合,在输出层之前加入dropout,可以用一个placeholder来代表在dropout中保持不变的概率;
保存和恢复模型两种方式:
(1)使用tf.train.Saver()对象,构造器给图中的所有变量,或定义在列表里的变量(位于tf.train.Saver之后的变量不会被保存),添加save和restore ops,saver对象提供了方法来运行这些ops,定义检查点文件的读写路径;
存储和恢复部分变量:给tf.train.Saver()构造函数传入python字典,可定义需要保持的变量及对应名称:键对应使用的名称,值对应被管理的变量;
(2)生成图协议文件,二进制文件,扩展名为.pb,用tf.train.write_graph()保存,只包含图形结构,不包含权重,然后使用tf.import_graph_def()来加载图形;
检查点文件(checkpoint file):可以用来后续恢复模型以进一步训练或评估;
队列和线程:
tf.FIFOQueue创建一个先入先出队列:enqueue_many(),enqueue()执行入队操作(以列表的的形式放入元素);dequeue()执行出队操作;
tf.RandomShuffleQueue创建一个随机队列(可以设置队列最大长度capacity,出队后最小长度min_after_dequeue)。出队列时以随机的顺序产生元素的(可以在CNN网络中随机产生一个训练样本);
队列管理器QueueRunner可以创建进程,弊端是入队线程独自执行,在需要的出队操作完成之后,程序无法结束;
协调器tf.train.Coordinator实现线程间的同步,终止其他线程;
以上是关于curl使用小记——获取远端数据到内存缓冲区的主要内容,如果未能解决你的问题,请参考以下文章