我在用C编写的项目中使用libcurl
。这个应用程序向服务器发出不同的请求,所以我决定制作几个帮助函数来配置CURL对象,发出请求和解析响应。这是curl包装器的代码:
typedef struct {
char *ptr;
size_t len;
} curl_str_t;
typedef size_t (*callback_ptr)(void *, size_t, size_t, void *);
typedef struct {
CURLoption curl_opt;
union {
callback_ptr calback;
void *obj_ptr;
long opt;
curl_off_t offset;
} opt_param;
} curl_opt_t;
int curl_routine_init_curl_str(curl_str_t *cs) {
cs->len = 0;
cs->ptr = malloc(cs->len + 1);
if (cs->ptr == NULL)
return -ENOMEM;
cs->ptr[0] = '\0';
return 0;
}
size_t curl_routine_curl_write_callback(void *ptr, size_t size, size_t nmeb,
curl_str_t *s) {
size_t new_len = s->len + size * nmeb;
s->ptr = realloc(s->ptr, new_len + 1);
if (s->ptr == NULL)
return -ENOMEM;
memcpy(s->ptr + s->len, ptr, size * nmeb);
s->ptr[new_len] = '\0';
s->len = new_len;
return size * nmeb;
}
size_t curl_routine_curl_read_callback(void *ptr, size_t size, size_t nmemb,
void *stream) {
curl_off_t nread;
size_t retcode = fread(ptr, size, nmemb, stream);
nread = (curl_off_t)retcode;
return nread;
}
static int curl_routine_curl_process_set_opt(CURL *curl, const curl_opt_t *opt) {
long option = (long)opt->curl_opt;
if (option - CURLOPTTYPE_OFF_T > 0)
return curl_easy_setopt(curl, opt->curl_opt, opt->opt_param.offset);
else if (option - CURLOPTTYPE_FUNCTIONPOINT > 0)
return curl_easy_setopt(curl, opt->curl_opt, opt->opt_param.calback);
#ifdef CURLOPTTYPE_STRINGPOINT
else if (option - CURLOPTTYPE_STRINGPOINT > 0)
return curl_easy_setopt(curl, opt->curl_opt, opt->opt_param.obj_ptr);
#endif
else if (option - CURLOPTTYPE_OBJECTPOINT > 0)
return curl_easy_setopt(curl, opt->curl_opt, opt->opt_param.obj_ptr);
else
return curl_easy_setopt(curl, opt->curl_opt, opt->opt_param.opt);
}
int curl_routine_curl_process(const char *url, int try_num, char **headers,
int headers_cnt, const curl_opt_t *opts, int opts_cnt, double *load_speed,
double *load_time) {
CURL *curl = NULL;
CURLcode curl_res = CURLE_OK;
char errbuf[CURL_ERROR_SIZE] = { '\0' };
struct curl_slist *http_headers = NULL;
int rc = EXIT_SUCCESS, delay = 2, i;
long http_code = 200;
#ifdef CURL_VERBOSE
curl_opt_t local_opts[8];
#else
curl_opt_t local_opts[7];
#endif
curl_routine_create_curl_opts(local_opts,
(sizeof(local_opts) / sizeof(curl_opt_t)) * 2
,CURLOPT_FAILONERROR, 1L
,CURLOPT_FOLLOWLOCATION, 1L
,CURLOPT_ERRORBUFFER, errbuf
,CURLOPT_SSL_VERIFYPEER, 0L
,CURLOPT_SSL_VERIFYHOST, 0L
,CURLOPT_TCP_KEEPALIVE, 1L
,CURLOPT_URL, url
#ifdef CURL_VERBOSE
,CURLOPT_VERBOSE, 1L
#endif
);
curl = curl_easy_init();
if (curl) {
if (headers) {
for (i = 0; i < headers_cnt; i++)
http_headers = curl_slist_append(http_headers, headers[i]);
curl_res = curl_easy_setopt(curl, CURLOPT_HTTPHEADER, http_headers);
if (curl_res != CURLE_OK) {
log_error("cURL return error: %s\n", curl_easy_strerror(curl_res));
rc = EXIT_FAILURE;
goto end;
}
}
if (opts)
for (i = 0; i < opts_cnt; i++) {
curl_res = curl_routine_curl_process_set_opt(curl, &opts[i]);
if (curl_res != CURLE_OK) {
log_error("cURL return error: %s\n", curl_easy_strerror(curl_res));
rc = EXIT_FAILURE;
goto end;
}
}
for (i = 0; i < (sizeof (local_opts) / sizeof (curl_opt_t)); i++) {
curl_res = curl_routine_curl_process_set_opt(curl, &local_opts[i]);
if (curl_res != CURLE_OK) {
log_error("cURL return error: %s\n", curl_easy_strerror(curl_res));
rc = EXIT_FAILURE;
goto end;
}
}
while (try_num--) {
memset(errbuf, '\0', CURL_ERROR_SIZE);
curl_res = curl_easy_perform(curl);
if (curl_res == CURLE_OK) {
curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &http_code);
if (load_speed)
curl_easy_getinfo(curl, CURLINFO_SPEED_UPLOAD, &load_speed);
if (load_time)
curl_easy_getinfo(curl, CURLINFO_TOTAL_TIME, &load_time);
log_debug("Request to '%s' returned with %lu code:%s\n",
url, http_code, errbuf);
if (http_code >= 200 && http_code < 300) {
rc = EXIT_SUCCESS;
break;
} else {
rc = http_code;
}
} else {
rc = curl_res;
log_error("Request to '%s' can't be executed: %s (%s)\n",
url, curl_easy_strerror(rc), errbuf);
break;
}
sleep(delay);
delay += 3;
}
} else {
log_error("Can't initialize curl\n");
rc = EXIT_FAILURE;
}
end:
curl_easy_cleanup(curl);
curl_slist_free_all(http_headers);
return rc;
}
int curl_routine_create_curl_opts(curl_opt_t *opts, int size, ...) {
va_list ap;
int i;
long option;
if (!opts)
return 0;
va_start(ap, size);
for (i = 0; i < size / 2; i++) {
opts[i].curl_opt = va_arg(ap, CURLoption);
option = (long)opts[i].curl_opt;
if (option - CURLOPTTYPE_OFF_T > 0)
opts[i].opt_param.offset = va_arg(ap, curl_off_t);
else if (option - CURLOPTTYPE_FUNCTIONPOINT > 0)
opts[i].opt_param.calback = va_arg(ap, callback_ptr);
#ifdef CURLOPTTYPE_STRINGPOINT
else if (option - CURLOPTTYPE_STRINGPOINT > 0)
opts[i].opt_param.obj_ptr = va_arg(ap, char *);
#endif
else if (option - CURLOPTTYPE_OBJECTPOINT > 0)
opts[i].opt_param.obj_ptr = va_arg(ap, void *);
else
opts[i].opt_param.opt = va_arg(ap, long);
}
va_end(ap);
return EXIT_SUCCESS;
}
以下是我如何使用它:
int communicate_with_server_tell_about_file(const processing_stack_item_t *item,
const config_t *config) {
int rc = EXIT_SUCCESS;
char *url = NULL;
char postfields[2048] = { '\0' };
curl_opt_t opts[1];
/* Some code */
if ((rc = curl_routine_create_curl_opts(opts, 2,
CURLOPT_POSTFIELDS, postfields)))
goto end;
rc = curl_routine_curl_process(url, 2, NULL, 0, opts, 1, NULL, NULL);
end:
free(url);
return rc;
}
还有一个
static int func2(const send_item_t *item) {
int rc = EXIT_SUCCESS;
char url[256] = { '\0' };
char *headers[5];
FILE *fp = NULL;
curl_opt_t opts[5];
long http_code;
double load_speed, load_time;
/* Some code */
fp = fopen(item->fpath, "rb");
if (!fp) {
rc = errno;
print_error("Can't open file '%s': %s", item->fpath, strerror(rc));
goto end;
}
if ((rc = curl_routine_create_curl_opts(opts, 10,
CURLOPT_PUT, 1L
,CURLOPT_UPLOAD, 1L
,CURLOPT_READDATA, fp
,CURLOPT_READFUNCTION, curl_routine_curl_read_callback
,CURLOPT_INFILESIZE, (curl_off_t)item->size
))) {
goto end;
}
headers[0] = CONT_TYPE_HEADER;
headers[1] = x_ms_date_h;
headers[2] = META_M1_HEADER;
headers[3] = META_M2_HEADER;
headers[4] = authorization_header;
#define MBs 1024 / 1024
rc = curl_routine_curl_process(url, 2, headers, 5, opts, 5,
&load_speed, &load_time);
if (!rc)
log_info("File '%s' sent. Speed: %.3f MB/s, Time: %.3f s\n",
item->name, load_speed / MBs, load_time);
else
log_error("Can't send file\n");
end:
free(authorization_header);
fclose(fp);
return rc;
}
所以,当我在我的x86_64计算机上运行构建程序时,一切都运行良好,但是当我为我的ARMV5e板func2
构建它时,工作非常奇怪:所有上传的文件都是0字节大小,它看起来像那样curl不会使用curl_routine_curl_read_callback
调用我传递给它的callback_ptr
。我尝试为其附加调试器并查看curl_routine_curl_read_callback
的地址和callback_ptr
的值 - 它们是相同的。
还有一个。如果我在func2
中重写代码,就像libcurl
(内联调用curl_easy_setopt
)的示例一样,一切都按照我的计划进行。
有谁能告诉我我做错了什么?或许我错过了什么?
提前致谢。
P.S。顺便说一句,我的编译器是gcc v 4.9.2,ARM板的libcurl
是4.3.0,ARM板的标准C库是uClib
v.0.9.33.2