前面的文章《Libcurl编译指南——Android和Windows系统》已经就libcurl在Windows和Android系统编做了详细的说明。
本文档用C/C++实现简单的HTTP/HTTPS客户端,支持get和post,支持保持session和长连接。
httpConn.h
#ifndef __HTTP_CONN_H__
#define __HTTP_CONN_H__
#include "curl.h"
#define RETRY_TIMES 3
struct MemoryStruct {
char *memory;
unsigned int size;
};
class RequestHandler
{
private:
public:
MemoryBlock request;
MemoryBlock response;
int http_code;
};
class HttpRequest
{
private:
CURL *curl_handle;
CURLcode curl_res;
long http_code;
struct MemoryStruct receive_header;
struct MemoryStruct receive_content;
struct MemoryStruct error_message;
struct curl_slist *http_headers;
int retry_times;
public:
HttpRequest();
~HttpRequest();
int setRequestUrl(const char *url); // 设置URL
int setRequestHeader(const char *header); // 设置Header
int setRequestTimeout(long time); // 设置超时时间
int setPostData(const char *data, unsigned int size); // 设置post数据
int setResultCallback(); // 回调
int performRequest(); // 发送数据
int sendReceiveEx(const char *url, RequestHandler *handler); // 发送和接收url请求
int getHttpCode(); //返回http code
int getHeader(char *header, unsigned int *len); // 返回Header
int getContent(char *content, unsigned int *len);
};
#endif
httpConn.cpp
#include "httpConn.h"
#include "curl.h"
static unsigned int RetriveContentCallback(void *contents, unsigned int size, unsigned int nmemb, void *userp)
{
unsigned int realsize = size * nmemb;
struct MemoryStruct *mem = (struct MemoryStruct *)userp;
char *ptr = (char*)realloc((void *)mem->memory, mem->size + realsize + 1);
if (ptr == NULL) {
/* out of memory! */
printf("not enough memory (realloc returned NULL)\n");
return -6;
}
mem->memory = ptr;
memcpy(&(mem->memory[mem->size]), contents, realsize);
mem->size += realsize;
mem->memory[mem->size] = 0;
return realsize;
}
static unsigned int RetriveHeaderCallback(void *contents, unsigned int size, unsigned int nmemb, void *userp)
{
unsigned int realsize = size * nmemb;
struct MemoryStruct *mem = (struct MemoryStruct *)userp;
char *ptr = (char*)realloc((void *)mem->memory, mem->size + realsize + 1);
if (ptr == NULL) {
/* out of memory! */
printf("not enough memory (realloc returned NULL)\n");
return -6;
}
mem->memory = ptr;
memcpy(&(mem->memory[mem->size]), contents, realsize);
mem->size += realsize;
mem->memory[mem->size] = 0;
return realsize;
}
HttpRequest::HttpRequest()
{
curl_res = CURLE_OK;
http_code = 0;
curl_handle = NULL;
http_headers = NULL;
retry_times = RETRY_TIMES;
/* will be grown as needed by the realloc above */
receive_content.memory = (char*)malloc(1);
receive_header.memory = (char*)malloc(1);
error_message.memory = (char*)malloc(1);
/* no data at this point */
receive_content.size = 0;
receive_header.size = 0;
/* global libcurl initialisation */
curl_global_init(CURL_GLOBAL_ALL);
/* init the curl session */
curl_handle = curl_easy_init();
}
HttpRequest::~HttpRequest()
{
/* cleanup curl stuff */
if (curl_handle)
{
curl_easy_cleanup(curl_handle);
}
free(receive_content.memory);
free(receive_header.memory);
free(error_message.memory);
if (http_headers)
{
curl_slist_free_all(http_headers);
}
/* we're done with libcurl, so clean it up */
curl_global_cleanup();
}
int HttpRequest::setRequestUrl(const char *url)
{
if (0 == memcmp(url, "https", 5))
{
curl_res = curl_easy_setopt(curl_handle, CURLOPT_SSL_VERIFYPEER, 0L);
if (CURLE_OK != curl_res)
{
printf("curl_easy_setopt CURLOPT_SSL_VERIFYPEER failed. curl_res = %d, curl_strerror = %s\n", curl_res, curl_easy_strerror(curl_res));
return -1;
}
curl_res = curl_easy_setopt(curl_handle, CURLOPT_SSL_VERIFYHOST, 0L);
if (CURLE_OK != curl_res)
{
printf("curl_easy_setopt CURLOPT_SSL_VERIFYHOST failed. curl_res = %d, curl_strerror = %s\n", curl_res, curl_easy_strerror(curl_res));
return -1;
}
}
#ifdef USE_PROXY
curl_res = curl_easy_setopt(curl_handle, CURLOPT_PROXY, PROXY_ADDRESS);
if (CURLE_OK != curl_res)
{
printf("curl_easy_setopt CURLOPT_PROXY failed. curl_res = %d, curl_strerror = %s\n", curl_res, curl_easy_strerror(curl_res));
return -1;
}
#endif // USE_PROXY
curl_res = curl_easy_setopt(curl_handle, CURLOPT_URL, url);
if (CURLE_OK != curl_res)
{
printf("curl_easy_setopt CURLOPT_URL failed. curl_res = %d, curl_strerror = %s\n", curl_res, curl_easy_strerror(curl_res));
return -1;
}
return 0;
}
int HttpRequest::setRequestHeader(const char * header)
{
http_headers = curl_slist_append(http_headers, header);
curl_res = http_headers ? CURLE_OK : CURLE_FAILED_INIT;
if (CURLE_OK != curl_res)
{
printf("Set request header: curl_slist_append failed.");
return -2;
}
return 0;
}
int HttpRequest::setRequestTimeout(long time)
{
curl_res = curl_easy_setopt(curl_handle, CURLOPT_TIMEOUT, time);
if (CURLE_OK != curl_res)
{
printf("curl_easy_setopt CURLOPT_TIMEOUT failed. curl_res = %d, curl_strerror = %s\n", curl_res, curl_easy_strerror(curl_res));
return -1;
}
return 0;
}
int HttpRequest::setPostData(const char *data, unsigned int size)
{
curl_res = curl_easy_setopt(curl_handle, CURLOPT_POST, 1);
if (CURLE_OK != curl_res)
{
printf("curl_easy_setopt CURLOPT_POST failed. curl_res = %d, curl_strerror = %s\n", curl_res, curl_easy_strerror(curl_res));
return -1;
}
curl_res = curl_easy_setopt(curl_handle, CURLOPT_POSTFIELDS, data);
if (CURLE_OK != curl_res)
{
printf("curl_easy_setopt CURLOPT_POSTFIELDS failed. curl_res = %d, curl_strerror = %s\n", curl_res, curl_easy_strerror(curl_res));
return -1;
}
curl_res = curl_easy_setopt(curl_handle, CURLOPT_POSTFIELDSIZE, size);
if (CURLE_OK != curl_res)
{
printf("curl_easy_setopt CURLOPT_POSTFIELDSIZE failed. curl_res = %d, curl_strerror = %s\n", curl_res, curl_easy_strerror(curl_res));
return -1;
}
return 0;
}
int HttpRequest::setResultCallback()
{
/* send all data to this function */
curl_res = curl_easy_setopt(curl_handle, CURLOPT_WRITEFUNCTION, RetriveContentCallback);
if (CURLE_OK != curl_res)
{
printf("curl_easy_setopt CURLOPT_WRITEFUNCTION failed. curl_res = %d, curl_strerror = %s\n", curl_res, curl_easy_strerror(curl_res));
return -1;
}
/* we pass our 'receive_content' struct to the callback function */
curl_res = curl_easy_setopt(curl_handle, CURLOPT_WRITEDATA, (void *)&receive_content);
if (CURLE_OK != curl_res)
{
printf("curl_easy_setopt CURLOPT_WRITEDATA failed. curl_res = %d, curl_strerror = %s\n", curl_res, curl_easy_strerror(curl_res));
return -1;
}
/*curl_res = curl_easy_setopt(curl_handle, CURLOPT_HEADERFUNCTION, RetriveHeaderCallback);
if (CURLE_OK != curl_res)
{
printf("curl_easy_setopt CURLOPT_HEADERFUNCTION failed. curl_res = %d, curl_strerror = %s\n", curl_res, curl_easy_strerror(curl_res));
return -1;
}
curl_res = curl_easy_setopt(curl_handle, CURLOPT_HEADERDATA, (void *)&receive_header);
if (CURLE_OK != curl_res)
{
printf("curl_easy_setopt CURLOPT_HEADERDATA failed. curl_res = %d, curl_strerror = %s\n", curl_res, curl_easy_strerror(curl_res));
return -1;
}*/
return 0;
}
int HttpRequest::performRequest()
{
if (curl_handle)
{
setResultCallback();
setRequestTimeout(10);
if (http_headers)
{
curl_res = curl_easy_setopt(curl_handle, CURLOPT_HTTPHEADER, http_headers);
if (CURLE_OK != curl_res)
{
printf("curl_easy_setopt CURLOPT_HTTPHEADER failed. curl_res = %d, curl_strerror = %s\n", curl_res, curl_easy_strerror(curl_res));
return -1;
}
}
/* some servers don't like requests that are made without a user-agent field, so we provide one */
curl_res = curl_easy_setopt(curl_handle, CURLOPT_USERAGENT, "libcurl-agent/1.0");
if (CURLE_OK != curl_res)
{
printf("curl_easy_setopt CURLOPT_USERAGENT failed. curl_res = %d, curl_strerror = %s\n", curl_res, curl_easy_strerror(curl_res));
return -1;
}
curl_res = curl_easy_setopt(curl_handle, CURLOPT_NOPROGRESS, 1);
if (CURLE_OK != curl_res)
{
printf("curl_easy_setopt CURLOPT_NOPROGRESS failed. curl_res = %d, curl_strerror = %s\n", curl_res, curl_easy_strerror(curl_res));
return -1;
}
curl_res = curl_easy_setopt(curl_handle, CURLOPT_NOSIGNAL, 1);
if (CURLE_OK != curl_res)
{
printf("curl_easy_setopt CURLOPT_NOSIGNAL failed. curl_res = %d, curl_strerror = %s\n", curl_res, curl_easy_strerror(curl_res));
return -1;
}
curl_res = curl_easy_setopt(curl_handle, CURLOPT_CONNECTTIMEOUT_MS, 0);
if (CURLE_OK != curl_res)
{
printf("curl_easy_setopt CURLOPT_CONNECTTIMEOUT_MS failed. curl_res = %d, curl_strerror = %s", curl_res, curl_easy_strerror(curl_res));
return -1;
}
/* get it! */
curl_res = curl_easy_perform(curl_handle);
if (curl_res == CURLE_OPERATION_TIMEDOUT)
{
int retry_count = retry_times;
while (retry_count > 0)
{
curl_res = curl_easy_perform(curl_handle);
if (curl_res != CURLE_OPERATION_TIMEDOUT) break;
retry_count--;
}
}
if (curl_res == CURLE_OPERATION_TIMEDOUT)
{
printf("curl_easy_perform failed. curl_res = %d, curl_strerror = %s\n", curl_res, curl_easy_strerror(curl_res));
return -3;
}
curl_easy_getinfo(curl_handle, CURLINFO_RESPONSE_CODE, &http_code);
if (curl_res != CURLE_OK || http_code != 200)
{
printf("curl_easy_perform failed. curl_res = %d, Httpcode = %ld, curl_strerror = %s\n", curl_res, http_code, curl_easy_strerror(curl_res));
return -4;
}
/*
* Now, our receive_content.memory points to a memory block that is receive_content.size
* bytes big and contains the remote file.
*
* Do something nice with it!
*/
}
return 0;
}
int HttpRequest::sendReceiveEx(const char * url, RequestHandler * handler)
{
int ret = 0;
int httpCpde = 0;
char receive[20480] = { 0 };
unsigned int rlen = sizeof(receive);
char requet[10240] = { 0 };
if (0 != handler->request.getSize())
{
sprintf(requet, "%s?%s#", url, handler->request.getData());
}
else
{
sprintf(requet, "%s", url);
}
if (receive_content.memory)
{
free(receive_content.memory);
receive_content.memory = NULL;
receive_content.memory = (char*)malloc(1);
receive_content.size = 0;
}
if (curl_handle)
{
// set params
curl_easy_reset(curl_handle);
/* enable TCP keep-alive for this transfer */
curl_easy_setopt(curl_handle, CURLOPT_TCP_KEEPALIVE, 1L);
if (CURLE_OK != curl_res)
{
printf("curl_easy_setopt CURLOPT_TCP_KEEPALIVE failed. curl_res = %d, curl_strerror = %s\n", curl_res, curl_easy_strerror(curl_res));
return -1;
}
/* keep-alive idle time to 120 seconds */
curl_easy_setopt(curl_handle, CURLOPT_TCP_KEEPIDLE, 120L);
if (CURLE_OK != curl_res)
{
printf("curl_easy_setopt CURLOPT_TCP_KEEPIDLE failed. curl_res = %d, curl_strerror = %s\n", curl_res, curl_easy_strerror(curl_res));
return -1;
}
/* interval time between keep-alive probes: 30 seconds */
curl_easy_setopt(curl_handle, CURLOPT_TCP_KEEPINTVL, 30L);
if (CURLE_OK != curl_res)
{
printf("curl_easy_setopt CURLOPT_TCP_KEEPINTVL failed. curl_res = %d, curl_strerror = %s\n", curl_res, curl_easy_strerror(curl_res));
return -1;
}
/* export cookies to this file when closing the handle */
curl_easy_setopt(curl_handle, CURLOPT_COOKIEJAR, "./cookies.txt");
if (CURLE_OK != curl_res)
{
printf("curl_easy_setopt CURLOPT_COOKIEJAR failed. curl_res = %d, curl_strerror = %s\n", curl_res, curl_easy_strerror(curl_res));
return -1;
}
/* get cookies from an existing file */
curl_easy_setopt(curl_handle, CURLOPT_COOKIEFILE, "./cookies.txt");
if (CURLE_OK != curl_res)
{
printf("curl_easy_setopt CURLOPT_COOKIEFILE failed. curl_res = %d, curl_strerror = %s\n", curl_res, curl_easy_strerror(curl_res));
return -1;
}
setResultCallback();
setRequestTimeout(10);
ret = setRequestUrl(url);
if (0 != ret)
{
return ret;
}
setPostData((const char *)handler->request.getData(), handler->request.getSize());
if (http_headers)
{
curl_res = curl_easy_setopt(curl_handle, CURLOPT_HTTPHEADER, http_headers);
if (CURLE_OK != curl_res)
{
printf("curl_easy_setopt CURLOPT_HTTPHEADER failed. curl_res = %d, curl_strerror = %s", curl_res, curl_easy_strerror(curl_res));
return -1;
}
}
curl_res = curl_easy_setopt(curl_handle, CURLOPT_NOPROGRESS, 1);
if (CURLE_OK != curl_res)
{
printf("curl_easy_setopt CURLOPT_NOPROGRESS failed. curl_res = %d, curl_strerror = %s\n", curl_res, curl_easy_strerror(curl_res));
return -1;
}
curl_res = curl_easy_setopt(curl_handle, CURLOPT_NOSIGNAL, 1);
if (CURLE_OK != curl_res)
{
printf("curl_easy_setopt CURLOPT_NOSIGNAL failed. curl_res = %d, curl_strerror = %s\n", curl_res, curl_easy_strerror(curl_res));
return -1;
}
curl_res = curl_easy_setopt(curl_handle, CURLOPT_CONNECTTIMEOUT_MS, 0);
if (CURLE_OK != curl_res)
{
printf("curl_easy_setopt CURLOPT_CONNECTTIMEOUT_MS failed. curl_res = %d, curl_strerror = %s\n", curl_res, curl_easy_strerror(curl_res));
return -1;
}
/* get it! */
curl_res = curl_easy_perform(curl_handle);
if (curl_res == CURLE_OPERATION_TIMEDOUT)
{
int retry_count = retry_times;
while (retry_count > 0)
{
curl_res = curl_easy_perform(curl_handle);
if (curl_res != CURLE_OPERATION_TIMEDOUT) break;
retry_count--;
}
}
if (curl_res == CURLE_OPERATION_TIMEDOUT)
{
printf("curl_easy_perform failed. curl_res = %d, curl_strerror = %s\n", curl_res, curl_easy_strerror(curl_res));
ret = -3;
}
http_code = 0;
curl_easy_getinfo(curl_handle, CURLINFO_RESPONSE_CODE, &http_code);
if (curl_res == CURLE_OK && http_code == 200)
{
/*
* Now, our receive_content.memory points to a memory block that is receive_content.size
* bytes big and contains the remote file.
*
* Do something nice with it!
*/
getContent(receive, &rlen);
char *response = (char*)handler->response.resize(rlen, rlen + 4, false);
memset(response, '\0', rlen+4);
memcpy(response, receive, rlen);
response[rlen] = '\0';
httpCpde = getHttpCode();
handler->http_code = httpCpde;
printf("sendReceive() <<< Data : \n%s\n[response data len : %d]\n", handler->response.getData(), handler->response.getSize());
ret = 0;
}
else
{
printf("curl_easy_perform failed. curl_res = %d, Httpcode = %ld, curl_strerror = %s\n", curl_res, http_code, curl_easy_strerror(curl_res));
return -4;
}
}
return ret;
}
int HttpRequest::getHttpCode()
{
return http_code;
}
int HttpRequest::getHeader(char *header, unsigned int *len)
{
if (receive_header.size > *len)
{
return -5;
}
memcpy(header, receive_header.memory, receive_header.size);
*len = receive_header.size;
return 0;
}
int HttpRequest::getContent(char *content, unsigned int *len)
{
if (receive_content.size > *len)
{
return -5;
}
memcpy(content, receive_content.memory, receive_content.size);
*len = receive_content.size;
return 0;
}
Demo
#include <stdio.h>
#include "httpConn.h"
// 封装自己的客户端
int customizeSendReceive(const char *url)
{
int ret = 0;
HttpRequest req;
int httpCpde = 0;
char receive[204800] = { 0 };
unsigned int rlen = sizeof(receive);
do
{
int urlLen = strlen(url);
if (0 >= urlLen)
{
break;
}
ret = req.setRequestUrl(url);
if (0 != ret)
{
break;
}
ret = req.performRequest();
if (0 != ret)
{
break;
}
httpCpde = req.getHttpCode();
ret = req.getContent(receive, &rlen);
if (0 != ret)
{
break;
}
} while (0);
if (0 == ret && 200 == httpCpde)
{
printf("ReceivevData :");
for (int i = 0; i < rlen; i++)
{
printf("%c", receive[i]);
}
printf("\n");
}
else
{
printf("sendReceive() failed.\n");
}
return ret;
}
// 使用httpClient已经实现的客户端
void testSys(const char *url, const char *post_data)
{
HttpRequest m_Conn;
RequestHandler handler;
handler.request.resize(0);
handler.response.resize(0);
int rlen = strlen(data);
char *request = (char*)handler->request.resize(rlen, rlen + 4, false);
memset(request, '\0', rlen+4);
memcpy(request, post_data, rlen);
int ret = m_Conn.sendReceiveEx(url, &handler);
if (0 != ret)
{
printf("sendReceiveEx failed, ret = %d\n", ret);
}
else
{
printf("response data : %s\n", (char *)handler.response.getData());
}
}
int main(int argc, char *argv[])
{
//customizeSendReceive("www.xxx.com?xxxxxx#");
//testSys("www.xxx.com", "?...#");
return 0;
}