近来一个新的项目需要使用到http。
本来用socket来写一个的,后来发现功能实在太简单,有点捉襟见肘。
于是改用libcur来做。
首先下载libcur的源码,然后配置:
./configure --prefix=$HOME/csource/linux/ CFLAGS='-O2 -m32 -fPIC' --enable-optimize --enable-static=libcurl.a --enable-ftp --without-zlib --disable-gopher --disable-rtsp --disable-dict --enable-proxy --disable-telnet --enable-tftp --disable-pop3 --disable-imap --enable-smtp --enable-ipv6 --enable-http -enable-crypto-auth --without-gnutls --without-nss --without-ca-bundle --with-random=/dev/urandom
然后编译
make && make install
代码如下,使用一个c++类来管理
重点代码是DownloadFile和UploadFile
#ifndef __MY_HTTP_CURL_H
#define __MY_HTTP_CURL_H #include <string> typedef long long LongSize; typedef int (*pCallBack)(double dtotal, double dnow); class CMYHttpClient
{
public:
CMYHttpClient();
~CMYHttpClient(); public:
/**
* @brief 下载请求, 支持断点续传
* @param strUrl 输入参数,请求的Url地址,如:http://www.baidu.com
* @param strFile 输入参数,本地存储的文件名
* @param timeout 输入参数,超时限制,0为永久等待
* @return 返回是否下载成功:true成功,false失败
*/
bool DownloadFile(const char *strUrl, const char *strFile, pCallBack cb = NULL, int timeout = );
/**
* @brief 获取将要下载的文件的大小,失败返回-1,成功返回非负
* @param strUrl 输入参数,请求的Url地址,如:http://www.baidu.com
* @return 返回文件的大小,失败返回-1
*/
LongSize GetDownloadFileSize(const char *strUrl);
/**
* @brief 上载请求, 支持断点续传
* @param strUrl 输入参数,请求的Url地址,如:http://www.baidu.com
* @param strFile 输入参数,请求上载的文件名
* @param timeout 输入参数,超时限制,0为永久等待
* @return 返回是否下载成功:true成功,false失败
*/
bool UploadFile(const char *strUrl, const char *strFile, pCallBack cb = NULL, int timeout = ); public:
/**
* @brief HTTP POST请求
* @param strUrl 输入参数,请求的Url地址,如:http://www.baidu.com
* @param strPost 输入参数,使用如下格式para1=val1¶2=val2&…
* @param strResponse 输出参数,返回的内容
* @param nResponse 输入输出参数,输入缓冲区大小,返回读入缓冲区的内容大小
* @param strFile 输入文件参数,返回的内容保存在这个文件中
* @param strHeader 输入参数,需要额外指定的http头,如果输入为NULL,则忽略
* @return 返回是否Post成功:0成功,非0失败
*/
int Post(const char *strUrl, const char* strPost, size_t nPost,
const char *strFile, const char* strHeader);
int Post(const char *strUrl, const char* strPost, size_t nPost,
char *strResponse, size_t &nResponse, const char *strHeader);
int Post(const std::string &strUrl, const std::string &strPost,
const char *strFile, const char* strHeader);
int Post(const std::string &strUrl, const std::string &strPost,
std::string &strResponse, const char *strHeader); /**
* @brief HTTP GET请求
* @param strUrl 输入参数,请求的Url地址,如:http://www.baidu.com
* @param strFile 输入文件参数,返回的内容保存在这个文件中
* @param strResponse 输出参数,返回的内容
* @param nResponse 输入输出参数,输入缓冲区大小,返回读入缓冲区的内容大小
* @return 返回是否Get成功:0成功,非0失败
*/
int Get(const char *strUrl, const char *strFile);
int Get(const char *strUrl, std::string &strResponse);
int Get(const char *strUrl, char *strResponse, size_t &nResponse);
int Get(const std::string &strUrl, char *strResponse, size_t &nResponse);
int Get(const std::string &strUrl, std::string &strResponse); /**
* @brief HTTPS POST请求,无证书版本
* @param strUrl 输入参数,请求的Url地址,如:https://www.alipay.com
* @param strPost 输入参数,使用如下格式para1=val1¶2=val2&…
* @param strResponse 输出参数,返回的内容
* @param strHeader 输入参数,需要额外指定的http头,如果输入为NULL,则忽略
* @param pCaPath 输入参数,为CA证书的路径.如果输入为NULL,则不验证服务器端证书的有效性.
* @return 返回是否Post成功:0成功,非0失败
*/
int Posts(const char *strUrl, const char *strPost, size_t nPost,
const char *strFile, const char *strHeader, const char *pCaPath);
int Posts(const std::string &strUrl, const std::string &strPost,
const char *strFile, const char *strHeader, const char *pCaPath);
int Posts(const char *strUrl, const char *strPost, size_t nPost,
char *strResponse, size_t &nResponse, const char *strHeader,
const char *pCaPath);
int Posts(const std::string &strUrl, const std::string &strPost,
std::string &strResponse, const char *strHeader, const char *pCaPath); /**
* @brief HTTPS GET请求,无证书版本
* @param strUrl 输入参数,请求的Url地址,如:https://www.alipay.com
* @param strResponse 输出参数,返回的内容
* @param pCaPath 输入参数,为CA证书的路径.如果输入为NULL,则不验证服务器端证书的有效性.
* @return 返回是否Post成功
*/
int Gets(const std::string &strUrl, std::string &strResponse,
const char *pCaPath = NULL); public:
void SetDebug(bool bDebug);
void AbortOperation(bool bAbort = true) { m_bAbort = bAbort; } private:
bool m_bDebug; //是否打开debug
bool m_bAbort; //是否放弃操作 std::string m_strFile; //需要下载的文件或者上传的文件 pCallBack m_fCB; //上传或者下载时的进度回调函数
LongSize m_lUploadPos; //上传文件的进度 static int OnProgress(void *pClient, curl_off_t dltotal, curl_off_t dlnow,
curl_off_t ultotal, curl_off_t ulnow);
static size_t OnWriteBuffer2File(char* buffer, size_t size, size_t nmemb, void* arg);
static size_t OnReadFile2Buffer(char *buffer, size_t size, size_t nmemb, void* arg);
}; #endif
几个主要方法,其中https的编译需要openssl,所以我没做支持。
#include "curl/curl.h"
#include <string>
#include <fstream>
#include <iostream>
#include <sstream> #include "HttpClient.h" using namespace std; CMYHttpClient::CMYHttpClient() :
#ifdef _DEBUG
m_bDebug(true),
#else
m_bDebug(false),
#endif
m_bAbort(false),
m_fCB(NULL),
m_lUploadPos()
{ } CMYHttpClient::~CMYHttpClient()
{ } static int OnDebug(CURL *, curl_infotype itype, char *pData, size_t size, void *)
{
if(itype == CURLINFO_TEXT)
{
//printf("[TEXT]%s\n", pData);
}
else if(itype == CURLINFO_HEADER_IN)
{
printf("[HEADER_IN]%s\n", pData);
}
else if(itype == CURLINFO_HEADER_OUT)
{
printf("[HEADER_OUT]%s\n", pData);
}
else if(itype == CURLINFO_DATA_IN)
{
printf("[DATA_IN]%s\n", pData);
}
else if(itype == CURLINFO_DATA_OUT)
{
printf("[DATA_OUT]%s\n", pData);
} return ;
} int CRGHttpClient::OnProgress(void *pClient, curl_off_t dltotal, curl_off_t dlnow,
curl_off_t ultotal, curl_off_t ulnow)
{
CMYHttpClient* pThis = (CMYHttpClient *)pClient;
if(NULL == pThis || pThis->m_bAbort)
{
//Returning a non-zero value from this callback will cause libcurl to abort
//the transfer and return CURLE_ABORTED_BY_CALLBACK.
return __LINE__;
}
else
{
if(pThis->m_fCB)
{
pThis->m_fCB((double)dltotal, (double)dlnow);
}
#ifdef _DEBUG
if(dltotal)
{
cout << "total bytes expects to download: " << dltotal
<< " and downloaded: " << dlnow << "\n";
}
else if(ultotal)
{
cout << "total bytes expects to upload: " << ultotal
<< " and uploaded: " << ulnow << "\n";
}
#endif return ;
}
} size_t CMYHttpClient::OnWriteBuffer2File(char* buffer, size_t size, size_t nmemb, void* arg)
{
CMYHttpClient* pThis = (CMYHttpClient *)arg;
if(NULL == pThis || pThis->m_bAbort ||
NULL == buffer)
{
return ;
} const char* strFile = pThis->m_strFile.c_str(); ofstream outfile(strFile, ofstream::binary | ofstream::app);
if(outfile.good())
{
const char* pData = (const char *)buffer;
outfile.write(pData, size * nmemb); return nmemb;
} return ;
} size_t CMYHttpClient::OnReadFile2Buffer(char *buffer, size_t size, size_t nmemb, void* arg)
{
CMYHttpClient* pThis = (CMYHttpClient *)arg;
if(NULL == pThis || NULL == buffer)
{
return ;
} ifstream infile(pThis->m_strFile.c_str(), ifstream::binary);
if(infile.good())
{
infile.seekg(pThis->m_lUploadPos, infile.beg);
if(infile.eof() == false)
{
infile.read(buffer, size * nmemb);
pThis->m_lUploadPos += infile.gcount();
return infile.gcount();
}
} return ;
} LongSize CMYHttpClient::GetDownloadFileSize(const char* strUrl)
{
if(NULL == strUrl)
return -; CURL* curl = curl_easy_init();
if(NULL == curl)
return -; curl_easy_setopt(curl, CURLOPT_URL, strUrl);
curl_easy_setopt(curl, CURLOPT_NOPROGRESS, );
curl_easy_setopt(curl, CURLOPT_NOBODY, );
CURLcode res = curl_easy_perform(curl);
if(res == CURLE_OK) {
double sz = ;
res = curl_easy_getinfo(curl, CURLINFO_CONTENT_LENGTH_DOWNLOAD, &sz); curl_easy_cleanup(curl); return (LongSize)sz;
}
else
{
cout << curl_easy_strerror(res) << endl;
} curl_easy_cleanup(curl); return -;
} bool CMYHttpClient::DownloadFile(const char* strUrl, const char* strFile, pCallBack cb, int timeout)
{
if(NULL == strUrl || NULL == strFile)
{
return false;
} //初始化curl库句柄
CURL* curl = curl_easy_init();
if(NULL == curl)
{
return false;
} m_strFile = string(strFile) + ".dl";
m_fCB = cb;
//支持断点续传,先获取文件大小,如果文件存在并且非空,则断点续传
ifstream infile(m_strFile.c_str(), ifstream::binary);
if(infile.good())
{
infile.seekg(, infile.end);
int length = infile.tellg();
infile.seekg(, infile.beg);
if(length > )
{
stringstream ss;
ss << length;
ss << "-";
LongSize ltotal = GetDownloadFileSize(strUrl);
if(ltotal > )
ss << ltotal;
string srange;
ss >> srange;
curl_easy_setopt(curl, CURLOPT_RANGE, srange.c_str());
}
}
infile.close(); CURLcode res;
if(m_bDebug)
{
curl_easy_setopt(curl, CURLOPT_VERBOSE, );
curl_easy_setopt(curl, CURLOPT_DEBUGFUNCTION, OnDebug);
} curl_easy_setopt(curl, CURLOPT_URL, strUrl);
curl_easy_setopt(curl, CURLOPT_READFUNCTION, NULL);
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, OnWriteBuffer2File);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, this); curl_easy_setopt(curl, CURLOPT_NOPROGRESS, );
curl_easy_setopt(curl, CURLOPT_XFERINFOFUNCTION, OnProgress);
curl_easy_setopt(curl, CURLOPT_XFERINFODATA, this); /**
* 当多个线程都使用超时处理的时候,同时主线程中有sleep或是wait等操作。
* 如果不设置这个选项,libcurl将会发信号打断这个wait从而导致程序退出。
*/
curl_easy_setopt(curl, CURLOPT_NOSIGNAL, );
curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, ); //wait for 4 seconds to connect to server
curl_easy_setopt(curl, CURLOPT_TIMEOUT, timeout); //0 means block always AbortOperation(false); //reset abort flag
res = curl_easy_perform(curl); curl_easy_cleanup(curl); if(res != CURLE_OK)
{
cout << curl_easy_strerror(res);
}
else
{
#ifdef _WIN32
DeleteFile(strFile);
MoveFile(m_strFile.c_str(), strFile);
#else
unlink(strFile);
rename(m_strFile.c_str(), strFile);
#endif
} m_strFile.clear();
m_fCB = NULL; return res == CURLE_OK;
} bool CMYHttpClient::UploadFile(const char *strUrl, const char *strFile, pCallBack cb, int timeout)
{
if(NULL == strUrl || NULL == strFile)
{
return false;
} //初始化curl库句柄
CURL* curl = curl_easy_init();
if(NULL == curl)
{
return false;
} CURLcode res;
m_fCB = cb;
m_strFile = strFile;
m_lUploadPos = ; curl_easy_setopt(curl, CURLOPT_URL, strUrl);
curl_easy_setopt(curl, CURLOPT_UPLOAD, 1L); curl_easy_setopt(curl, CURLOPT_READFUNCTION, OnReadFile2Buffer);
curl_easy_setopt(curl, CURLOPT_READDATA, this);
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, NULL); curl_easy_setopt(curl, CURLOPT_NOPROGRESS, );
curl_easy_setopt(curl, CURLOPT_XFERINFOFUNCTION, OnProgress);
curl_easy_setopt(curl, CURLOPT_XFERINFODATA, this); curl_easy_setopt(curl, CURLOPT_NOSIGNAL, );
curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, ); //wait for 4 seconds to connect to server
curl_easy_setopt(curl, CURLOPT_TIMEOUT, timeout); //0 means block always AbortOperation(false); //reset abort flag
res = curl_easy_perform(curl); curl_easy_cleanup(curl); m_strFile.clear();
m_fCB = NULL; if(res != CURLE_OK)
{
cout << curl_easy_strerror(res);
} return res == CURLE_OK;
}