实现HTTP协议Get、Post和文件上传功能——使用libcurl接口实现

http://blog.csdn.net/earbao/article/details/19615147

http://blog.sina.com.cn/s/blog_6e2d53050101k230.html

之前我们已经详细介绍了WinHttp接口如何实现Http的相关功能。本文我将主要讲解如何使用libcurl库去实现相关功能。(转载请指明出于breaksoftware的csdn博客)

        libcurl在http://curl.haxx.se/libcurl/有详细的介绍,有兴趣的朋友可以去读下。本文我只是从实际使用的角度讲解其中的一些功能。

        libcurl中主要有两个接口类型:CURL和CURLM。CURL又称easy interface,它接口简单、使用方便,但是它是一个同步接口,我们不能使用它去实现异步的功能——比如下载中断——其实也是有办法的(比如对写回调做点手脚)。相应的,CURLM又称multi interface,它是异步的。可以想下,我们使用easy interface实现一个HTTP请求过程,如果某天我们需要将其改成multi interface接口的,似乎需要对所有接口都要做调整。其实不然,libcurl使用一种优雅的方式去解决这个问题——multi interface只是若干个easy interface的集合。我们只要把easy interface指针加入到multi interface中即可。

[cpp]  view plain  copy
  1. CURLMcode curl_multi_add_handle(CURLM *multi_handle, CURL *easy_handle);  

        本文将使用multi interface作为最外层的管理者,具体下载功能交给easy interface。在使用easy interface之前,我们需要对其初始化

初始化

初始化easy interface

[cpp]  view plain  copy
  1. bool CHttpRequestByCurl::Prepare() {  
  2.     bool bSuc = false;  
  3.     do {  
  4.         if (!m_pCurlEasy) {  
  5.             m_pCurlEasy = curl_easy_init();  
  6.         }  
  7.         if (!m_pCurlEasy) {  
  8.             break;  
  9.         }  

初始化multi interface

[cpp]  view plain  copy
  1. if (!m_pCurlMulti){  
  2.     m_pCurlMulti = curl_multi_init();  
  3. }  
  4. if (!m_pCurlMulti) {  
  5.     break;  
  6. }  

设置

设置过程回调

        过程回调用于体现数据下载了多少或者上传了多少

[cpp]  view plain  copy
  1. CURLcode easycode;  
  2. easycode = curl_easy_setopt( m_pCurlEasy, CURLOPT_NOPROGRESS, 0 );  
  3. CHECKCURLEASY_EROORBREAK(easycode);  
  4. easycode = curl_easy_setopt(m_pCurlEasy, CURLOPT_PROGRESSFUNCTION, progresscallback);  
  5. CHECKCURLEASY_EROORBREAK(easycode);  
  6. easycode = curl_easy_setopt( m_pCurlEasy, CURLOPT_PROGRESSDATA, this );  
  7. CHECKCURLEASY_EROORBREAK(easycode);  

        设置CURLOPT_NOPROGRESS代表我们需要使用过程回调这个功能。设置CURLOPT_PROGRESSFUNCTION为progresscallback是设置回调函数的指针,我们将通过静态函数progresscallback反馈过程状态。注意一下这儿,因为libcurl是一个C语言API库,所以它没有类的概念,这个将影响之后我们对各种静态回调函数的设置。此处要求progresscallback是一个静态函数——它也没有this指针,但是libcurl设计的非常好,它留了一个用户自定义参数供我们使用,这样我们便可以将对象的this指针通过CURLOPT_PROGRESSDATA传过去。

[cpp]  view plain  copy
  1. int CHttpRequestByCurl::progresscallback( void *clientp, double dltotal, double dlnow, double ultotal, double ulnow ) {  
  2.     if (clientp) {  
  3.         CHttpRequestByCurl* pThis = (CHttpRequestByCurl*)clientp;  
  4.         return pThis->ProcessCallback(dltotal, dlnow);  
  5.     }  
  6.     else {  
  7.         return -1;  
  8.     }  
  9. }  
  10.   
  11.    int CHttpRequestByCurl::ProcessCallback( double dltotal, double dlnow ) {  
  12.        if ( m_CallBack ) {  
  13.            const DWORD dwMaxEslapeTime = 500;  
  14.            std::ostringstream os;  
  15.            os << (unsigned long)dlnow;  
  16.            std::string strSize = os.str();  
  17.   
  18.            std::ostringstream ostotal;  
  19.            ostotal << (unsigned long)dltotal;  
  20.            std::string strContentSize = ostotal.str();  
  21.            DWORD dwTickCount = GetTickCount();  
  22.            if ( ( 0 != ((unsigned long)dltotal)) && ( strSize == strContentSize || dwTickCount - m_dwLastCallBackTime > dwMaxEslapeTime ) ) {  
  23.                m_dwLastCallBackTime = dwTickCount;  
  24.                m_CallBack( strContentSize, strSize );  
  25.            }  
  26.        }  
  27.        return 0;  
  28.    }  

        此处progresscallback只是一个代理功能——它是静态的,它去调用clientp传过来的this指针所指向对象的ProcessCallback成员函数。之后我们的其他回调函数也是类似的,比如写结果的回调设置

设置写结果回调

[cpp]  view plain  copy
  1. easycode = curl_easy_setopt(m_pCurlEasy, CURLOPT_WRITEFUNCTION, writefilecallback);  
  2. CHECKCURLEASY_EROORBREAK(easycode);  
  3. easycode = curl_easy_setopt(m_pCurlEasy, CURLOPT_WRITEDATA, this);  
  4. CHECKCURLEASY_EROORBREAK(easycode);  
[cpp]  view plain  copy
  1. size_t CHttpRequestByCurl::writefilecallback( void *buffer, size_t size, size_t nmemb, void *stream ) {  
  2.     if (stream) {  
  3.          CHttpRequestByCurl* pThis = (CHttpRequestByCurl*)stream;  
  4.          return pThis->WriteFileCallBack(buffer, size, nmemb);  
  5.     }  
  6.     else {  
  7.         return size * nmemb;  
  8.     }  
  9. }  
  10.   
  11.    size_t CHttpRequestByCurl::WriteFileCallBack( void *buffer, size_t size, size_t nmemb ) {  
  12.        if (!m_pCurlEasy) {  
  13.            return 0;  
  14.        }  
  15.   
  16.        int nResponse = 0;  
  17.        CURLcode easycode = curl_easy_getinfo(m_pCurlEasy, CURLINFO_RESPONSE_CODE, &nResponse);  
  18.        if ( CURLE_OK != easycode || nResponse >= 400 ) {  
  19.            return 0;  
  20.        }  
  21.   
  22.        return Write(buffer, size, nmemb);  
  23.    }  
        在WriteFileCallBack函数中,我们使用curl_easy_getinfo判断了easy interface的返回值,这是为了解决接收返回结果时服务器中断的问题。

设置读回调

        读回调我们并没有传递this指针过去。

[cpp]  view plain  copy
  1. easycode = curl_easy_setopt( m_pCurlEasy,  CURLOPT_READFUNCTION,  read_callback);  
  2. CHECKCURLEASY_EROORBREAK(easycode);  

        我们看下回调就明白了

[cpp]  view plain  copy
  1. size_t CHttpRequestByCurl::read_callback( void *ptr, size_t size, size_t nmemb, void *stream ) {  
  2.    return ((ToolsInterface::LPIMemFileOperation)(stream))->MFRead(ptr, size, nmemb);  
  3. }  
        这次用户自定义指针指向了一个IMemFileOperation对象指针,它是在之后的其他步奏里传递过来的。这儿有个非常有意思的地方——即MFRead的返回值和libcurl要求的read_callback返回值是一致的——并不是说类型一致——而是返回值的定义一致。这就是统一成标准接口的好处。

设置URL

[cpp]  view plain  copy
  1. easycode = curl_easy_setopt(m_pCurlEasy, CURLOPT_URL, m_strUrl.c_str());  
  2. CHECKCURLEASY_EROORBREAK(easycode);  

设置超时时间

[cpp]  view plain  copy
 
  1. easycode = curl_easy_setopt(m_pCurlEasy, CURLOPT_TIMEOUT_MS, m_nTimeout);  
  2. CHECKCURLEASY_EROORBREAK(easycode);  

设置Http头

[cpp]  view plain  copy
 
  1. for ( ToolsInterface::ListStrCIter it = m_listHeaders.begin(); it != m_listHeaders.end(); it++ ) {  
  2.     m_pHeaderlist = curl_slist_append(m_pHeaderlist, it->c_str());  
  3. }  
  4. if (m_pHeaderlist) {  
  5.     curl_easy_setopt(m_pCurlEasy, CURLOPT_HTTPHEADER, m_pHeaderlist);  
  6. }  

        这儿需要注意的是m_pHeaderlist在整个请求完毕后需要释放

[cpp]  view plain  copy
 
  1. if (m_pHeaderlist) {  
  2.     curl_slist_free_all (m_pHeaderlist);  
  3.     m_pHeaderlist = NULL;  
  4. }  

设置Agent

[cpp]  view plain  copy
 
  1. if (!m_strAgent.empty()) {  
  2.     easycode = curl_easy_setopt(m_pCurlEasy, CURLOPT_USERAGENT, m_strAgent.c_str());  
  3.     CHECKCURLEASY_EROORBREAK(easycode);  
  4. }  

设置Post参数

[cpp]  view plain  copy
 
  1. if ( ePost == GetType() ) {  
  2.     easycode = ModifyEasyCurl(m_pCurlEasy, m_Params);  
  3.     CHECKCURLEASY_EROORBREAK(easycode);  
  4. }  

        之后我们将讲解ModifyEasyCurl的实现。我们先把整个调用过程将完。

将easy interface加入到multi interface

[cpp]  view plain  copy
 
  1.       CURLMcode multicode = curl_multi_add_handle( m_pCurlMulti, m_pCurlEasy );  
  2.       CHECKCURLMULTI_EROORBREAK(multicode);  
  3.   
  4.       bSuc = true;  
  5. while (0);  
  6. return bSuc;  

运行

[cpp]  view plain  copy
 
  1. EDownloadRet CHttpRequestByCurl::Curl_Multi_Select(CURLM* pMultiCurl)  
  2. {  
  3.     EDownloadRet ERet = EContinue;  
  4.   
  5.     do {  
  6.         struct timeval timeout;  
  7.         fd_set fdread;  
  8.         fd_set fdwrite;  
  9.         fd_set fdexcep;  
  10.   
  11.         CURLMcode multicode;  
  12.         long curl_timeo = -1;  
  13.   
  14.         /* set a suitable timeout to fail on */   
  15.         timeout.tv_sec = 30; /* 30 seconds */   
  16.         timeout.tv_usec = 0;  
  17.         multicode = curl_multi_timeout(pMultiCurl, &curl_timeo);  
  18.         if ( CURLM_OK == multicode && curl_timeo >= 0 ) {  
  19.             timeout.tv_sec = curl_timeo / 1000;  
  20.             if (timeout.tv_sec > 1) {  
  21.                 timeout.tv_sec = 0;  
  22.             }   
  23.             else {  
  24.                 timeout.tv_usec = (curl_timeo % 1000) * 1000;  
  25.             }  
  26.         }  
  27.   
  28.         int nMaxFd = -1;  
  29.   
  30.         while ( -1 == nMaxFd ) {  
  31.   
  32.             FD_ZERO(&fdread);  
  33.             FD_ZERO(&fdwrite);  
  34.             FD_ZERO(&fdexcep);  
  35.   
  36.             multicode = curl_multi_fdset( m_pCurlMulti, &fdread, &fdwrite, &fdexcep, &nMaxFd );  
  37.             CHECKCURLMULTI_EROORBREAK(multicode);  
  38.             if ( -1 != nMaxFd ) {  
  39.                 break;  
  40.             }  
  41.             else {  
  42.                 if (WAIT_TIMEOUT != WaitForSingleObject(m_hStop, 100)) {  
  43.                     ERet = EInterrupt;  
  44.                     break;  
  45.                 }  
  46.                 int nRunning = 0;  
  47.                 CURLMcode multicode = curl_multi_perform( m_pCurlMulti, &nRunning );  
  48.                 CHECKCURLMULTI_EROORBREAK(multicode);  
  49.             }  
  50.         }  
  51.   
  52.         if ( EContinue == ERet ) {  
  53.             int nSelectRet = select( nMaxFd + 1, &fdread, &fdwrite, &fdexcep, &timeout );  
  54.   
  55.             if ( -1 == nSelectRet ){  
  56.                 ERet = EFailed;  
  57.             }  
  58.         }  
  59.         if ( EInterrupt == ERet ) {  
  60.             break;  
  61.         }  
  62.     } while (0);  
  63.   
  64.     return ERet;  
  65. }  
  66.   
  67. DWORD CHttpRequestByCurl::StartRequest() {  
  68.     Init();  
  69.     EDownloadRet eDownloadRet = ESuc;  
  70.     do {  
  71.         if (!Prepare()) {  
  72.             break;  
  73.         }  
  74.   
  75.         int nRunning = -1;  
  76.         while( CURLM_CALL_MULTI_PERFORM == curl_multi_perform(m_pCurlMulti, &nRunning) ) {  
  77.             if (WAIT_TIMEOUT != WaitForSingleObject(m_hStop, 10)) {  
  78.                 eDownloadRet = EInterrupt;  
  79.                 break;  
  80.             }  
  81.         }  
  82.   
  83.         if ( EInterrupt == eDownloadRet ) {  
  84.             break;  
  85.         }  
  86.   
  87.         while(0 != nRunning) {  
  88.             EDownloadRet nSelectRet = Curl_Multi_Select(m_pCurlMulti);  
  89.             if ( EFailed == nSelectRet || EInterrupt == nSelectRet || ENetError == nSelectRet ) {  
  90.                 eDownloadRet = nSelectRet;  
  91.                 break;  
  92.             }  
  93.             else {  
  94.                 CURLMcode multicode = curl_multi_perform(m_pCurlMulti, &nRunning);  
  95.                 if (CURLM_CALL_MULTI_PERFORM == multicode) {  
  96.                     if (WAIT_TIMEOUT != WaitForSingleObject(m_hStop, 10)) {  
  97.                         eDownloadRet = EInterrupt;  
  98.                         break;  
  99.                     }  
  100.                 }  
  101.                 else if ( CURLM_OK == multicode ) {  
  102.                 }  
  103.                 else {  
  104.                     if (WAIT_TIMEOUT != WaitForSingleObject(m_hStop, 100)) {  
  105.                         eDownloadRet = EInterrupt;  
  106.                     }  
  107.                     break;  
  108.                 }  
  109.             }  
  110.   
  111.             if ( EInterrupt == eDownloadRet ) {  
  112.                 break;  
  113.             }  
  114.         } // while  
  115.   
  116.         if ( EInterrupt == eDownloadRet ) {  
  117.             break;  
  118.         }  
  119.   
  120.         int msgs_left;    
  121.         CURLMsg*  msg;    
  122.         while((msg = curl_multi_info_read(m_pCurlMulti, &msgs_left))) {    
  123.             if (CURLMSG_DONE == msg->msg) {   
  124.                 if ( CURLE_OK != msg->data.result ) {  
  125.                     eDownloadRet = EFailed;  
  126.                 }  
  127.             }  
  128.             else {  
  129.                 eDownloadRet = EFailed;  
  130.             }  
  131.         }  
  132.   
  133.     } while (0);  
  134.   
  135.     Unint();  
  136.   
  137.     m_bSuc = ( ESuc == eDownloadRet ) ? true : false;  
  138.     return eDownloadRet;  
  139. }  

        可以见得运行的主要过程就是不停的调用curl_multi_perform。

实现Post、文件上传功能

        对于MultiPart格式数据,我们要使用curl_httppost结构体保存参数

组装上传文件

[cpp]  view plain  copy
 
  1. CURLcode CPostByCurl::ModifyEasyCurl_File( CURL* pEasyCurl, const FMParam& Param ) {  
  2.   
  3.     Param.value->MFSeek(0L, SEEK_END);  
  4.     long valuesize = Param.value->MFTell();  
  5.     Param.value->MFSeek(0L, SEEK_SET);  
  6.   
  7.     curl_formadd((curl_httppost**)&m_pFormpost,  
  8.         (curl_httppost**)&m_pLastptr,  
  9.         CURLFORM_COPYNAME, Param.strkey.c_str(),  
  10.         CURLFORM_STREAM, Param.value,   
  11.         CURLFORM_CONTENTSLENGTH, valuesize,  
  12.         CURLFORM_FILENAME, Param.fileinfo.szfilename,  
  13.         CURLFORM_CONTENTTYPE, "application/octet-stream",  
  14.         CURLFORM_END);  
  15.   
  16.     return CURLE_OK;  
  17. }  

        我们使用CURLFORM_STREAM标记数据的载体,此处我们传递的是一个IMemFileOperation指针,之前我们定义的readcallback回调将会将该参数作为第一个参数被调用。CURLFORM_CONTENTSLENGTH也是个非常重要的参数。如果我们不设置CURLFORM_CONTENTSLENGTH,则传递的数据长度是数据起始至\0结尾。所以我们在调用curl_formadd之前先计算了数据的长度——文件的大小。然后指定CURLFORM_FILENAME为服务器上保存的文件名。

组装上传数据

[cpp]  view plain  copy
 
  1. CURLcode CPostByCurl::ModifyEasyCurl_Mem( CURL* pEasyCurl, const FMParam& Param ) {  
  2.     if (Param.meminfo.bMulti) {  
  3.         Param.value->MFSeek(0L, SEEK_END);  
  4.         long valuesize = Param.value->MFTell();  
  5.         Param.value->MFSeek(0L, SEEK_SET);  
  6.         curl_formadd(&m_pFormpost, &m_pLastptr,   
  7.             CURLFORM_COPYNAME, Param.strkey.c_str(),   
  8.             CURLFORM_STREAM, Param.value,   
  9.             CURLFORM_CONTENTSLENGTH, valuesize,  
  10.             CURLFORM_CONTENTTYPE, "application/octet-stream",  
  11.             CURLFORM_END );  
  12.     }  
  13.     else {  
  14.         if (!m_strCommonPostData.empty()) {  
  15.             m_strCommonPostData += "&";  
  16.         }  
  17.         std::string strpostvalue;  
  18.         while(!Param.value->MFEof()) {  
  19.             char buffer[1024] = {0};  
  20.             size_t size = Param.value->MFRead(buffer, 1, 1024);  
  21.             strpostvalue.append(buffer, size);  
  22.         }  
  23.         m_strCommonPostData += Param.strkey;  
  24.         m_strCommonPostData += "=";  
  25.         m_strCommonPostData += strpostvalue;  
  26.     }  
  27.     return CURLE_OK;  
  28. }  
        对于需要MultiPart格式发送的数据,我们发送的方法和文件发送相似——只是少了CURLFORM_FILENAME设置——因为没有文件名。

        对于普通Post数据,我们使用m_strCommonPostData拼接起来。待之后一并发送。

设置数据待上传        

[cpp]  view plain  copy
 
  1. CURLcode CPostByCurl::ModifyEasyCurl( CURL* pEasyCurl, const FMParams& Params ) {  
  2.         for (FMParamsCIter it = m_PostParam.begin(); it != m_PostParam.end(); it++ ) {  
  3.             if (it->postasfile) {  
  4.                 ModifyEasyCurl_File(pEasyCurl, *it);  
  5.             }  
  6.             else {  
  7.                 ModifyEasyCurl_Mem(pEasyCurl, *it);  
  8.             }  
  9.         }  
  10.   
  11.         if (m_pFormpost){  
  12.             curl_easy_setopt(pEasyCurl, CURLOPT_HTTPPOST, m_pFormpost);  
  13.         }  
  14.   
  15.         if (!m_strCommonPostData.empty()) {  
  16.             curl_easy_setopt(pEasyCurl, CURLOPT_COPYPOSTFIELDS, m_strCommonPostData.c_str());  
  17.         }  
  18.   
  19.     return CURLE_OK;  
  20. }  
        通过设置CURLOPT_HTTPPOST,我们将MultiPart型数据——包括文件上传数据设置好。通过设置CURLOPT_COPYPOSTFIELDS,我们将普通Post型数据设置好。

        Get型请求没什么好说的。详细见之后给的工程源码。

        工程源码链接:http://pan.baidu.com/s/1i3eUnMt 密码:hfro


以下是一个例子:

  1. #include <stdio.h>  
  2. #include <curl/curl.h>  
  3. #include <string.h>  
  4. #include <ctype.h>  
  5. #include <iconv.h>  
  6. #define TMP_FILE "tmp.html"  
  7. #define HTML_BUFFER_SIZE 1024*800  
  8.   
  9. void split(char **arr, char *str, const char *del) {  
  10.     char *s = strtok(str, del);  
  11.     while (s != NULL) {  
  12.         *arr++ = s;  
  13.         s = strtok(NULL, del);  
  14.     }  
  15. }  
  16.   
  17. /*将str1字符串中第一次出现的str2字符串替换成str3*/  
  18. void replaceFirst(char *str1, char *str2, char *str3) {  
  19.     char str4[strlen(str1) + 1];  
  20.     char *p;  
  21.     strcpy(str4, str1);  
  22.     if ((p = strstr(str1, str2)) != NULL)/*p指向str2在str1中第一次出现的位置*/ {  
  23.         while (str1 != p && str1 != NULL)/*将str1指针移动到p的位置*/ {  
  24.             str1++;  
  25.         }  
  26.         str1[0] = '/0'/*将str1指针指向的值变成/0,以此来截断str1,舍弃str2及以后的内容,只保留str2以前的内容*/  
  27.         strcat(str1, str3); /*在str1后拼接上str3,组成新str1*/  
  28.         strcat(str1, strstr(str4, str2) + strlen(str2)); /*strstr(str4,str2)是指向str2及以后的内容(包括str2),strstr(str4,str2)+strlen(str2)就是将指针向前移动strlen(str2)位,跳过str2*/  
  29.     }  
  30. }  
  31.   
  32. /*将str1出现的所有的str2都替换为str3*/  
  33. void replace(char *str1, char *str2, char *str3) {  
  34.     while (strstr(str1, str2) != NULL) {  
  35.         replaceFirst(str1, str2, str3);  
  36.     }  
  37. }  
  38.   
  39. /*截取src字符串中,从下标为start开始到end-1(end前面)的字符串保存在dest中(下标从0开始)*/  
  40. void substring(char *dest, char *src, int start, int end) {  
  41.     char *p = src;  
  42.     int i = start;  
  43.     if (start > strlen(src))return;  
  44.     if (end > strlen(src))  
  45.         end = strlen(src);  
  46.     while (i < end) {  
  47.         dest[i - start] = src[i];  
  48.         i++;  
  49.     }  
  50.     dest[i - start] = '/0';  
  51.     return;  
  52. }  
  53.   
  54. /*返回src中下标为index的字符*/  
  55. char charAt(char *src, int index) {  
  56.     char *p = src;  
  57.     int i = 0;  
  58.     if (index < 0 || index > strlen(src))  
  59.         return 0;  
  60.     while (i < index)i++;  
  61.     return p[i];  
  62. }  
  63.   
  64. /*返回str2第一次出现在str1中的位置(下表索引),不存在返回-1*/  
  65. int indexOf(char *str1, char *str2) {  
  66.     char *p = str1;  
  67.     int i = 0;  
  68.     p = strstr(str1, str2);  
  69.     if (p == NULL)  
  70.         return -1;  
  71.     else {  
  72.         while (str1 != p) {  
  73.             str1++;  
  74.             i++;  
  75.         }  
  76.     }  
  77.     return i;  
  78. }  
  79.   
  80. /*返回str1中最后一次出现str2的位置(下标),不存在返回-1*/  
  81. int lastIndexOf(char *str1, char *str2) {  
  82.     char *p = str1;  
  83.     int i = 0, len = strlen(str2);  
  84.     p = strstr(str1, str2);  
  85.     if (p == NULL)return -1;  
  86.     while (p != NULL) {  
  87.         for (; str1 != p; str1++)i++;  
  88.         p = p + len;  
  89.         p = strstr(p, str2);  
  90.     }  
  91.     return i;  
  92. }  
  93.   
  94. /*删除str左边第一个非空白字符前面的空白字符(空格符和横向制表符)*/  
  95. void ltrim(char *str) {  
  96.     int i = 0, j, len = strlen(str);  
  97.     while (str[i] != '/0') {  
  98.         if (str[i] != 32 && str[i] != 9)break/*32:空格,9:横向制表符*/  
  99.         i++;  
  100.     }  
  101.     if (i != 0)  
  102.         for (j = 0; j <= len - i; j++) {  
  103.             str[j] = str[j + i]; /*将后面的字符顺势前移,补充删掉的空白位置*/  
  104.         }  
  105. }  
  106.   
  107. /*删除str最后一个非空白字符后面的所有空白字符(空格符和横向制表符)*/  
  108. void rtrim(char *str) {  
  109.     char *p = str;  
  110.     int i = strlen(str) - 1;  
  111.     while (i >= 0) {  
  112.         if (p[i] != 32 && p[i] != 9)break;  
  113.         i--;  
  114.     }  
  115.     str[++i] = '/0';  
  116. }  
  117.   
  118. /*删除str两端的空白字符*/  
  119. void trim(char *str) {  
  120.     ltrim(str);  
  121.     rtrim(str);  
  122. }  
  123.   
  124. //这是libcurl接收数据的回调函数,相当于recv的死循环  
  125. //其中stream可以自定义数据类型,这里我传入的是文件保存路径  
  126.   
  127. static size_t write_callback(void *ptr, size_t size, size_t nmemb, void *stream) {  
  128.     int len = size * nmemb;  
  129.     int written = len;  
  130.     FILE *fp = NULL;  
  131.     if (access((char*) stream, 0) == -1) {  
  132.         fp = fopen((char*) stream, "wb");  
  133.     } else {  
  134.         fp = fopen((char*) stream, "ab");  
  135.     }  
  136.     if (fp) {  
  137.         fwrite(ptr, size, nmemb, fp);  
  138.     }  
  139.     // printf("%s\n",ptr);  
  140.     fclose(fp);  
  141.     return written;  
  142. }  
  143. //加上-lcurl库  
  144.   
  145. void test_post(char* url,char* data) {  
  146.     CURL *curl;  
  147.     CURLcode res;  
  148.     curl = curl_easy_init();  
  149.     if (curl) {  
  150.         //www.baidu.com/#wd=java  
  151.         curl_easy_setopt(curl, CURLOPT_URL, url);  
  152.         curl_easy_setopt(curl, CURLOPT_POST, 1L);  
  153.         curl_easy_setopt(curl, CURLOPT_POSTFIELDS, data);  
  154.         res = curl_easy_perform(curl);  
  155.         curl_easy_cleanup(curl);  
  156.     }  
  157. }  
  158.   
  159. int file_exists(char *filename) {  
  160.     return (access(filename, 0) == 0);  
  161. }  
  162. int GetCharset(char *src_html,char *charCode) {  
  163.     char tmp_html[HTML_BUFFER_SIZE]={0};  
  164.     int pos = indexOf(src_html, "text/html; charset=");  
  165.     if (pos > 0) {  
  166.         strncpy(tmp_html, src_html + pos + strlen("text/html; charset="), strlen(src_html) - pos);  
  167.         pos = indexOf(tmp_html, "\"");  
  168.         if (pos > 0) {  
  169.             strncpy(charCode, tmp_html, pos);  
  170.         }  
  171.     }  
  172.     return 0;  
  173.   
  174. }  
  175.   
  176. void test_get(char* url) {  
  177.     CURL *curl;  
  178.     CURLcode res;  
  179.     curl = curl_easy_init();  
  180.     if (curl) {  
  181.         if (file_exists(TMP_FILE))  
  182.             remove(TMP_FILE);  
  183.         curl_easy_setopt(curl, CURLOPT_URL, url);  
  184.         //指定回调函数  
  185.         curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_callback);  
  186.         //这个变量可作为接收或传递数据的作用  
  187.         curl_easy_setopt(curl, CURLOPT_WRITEDATA, TMP_FILE);  
  188.         res = curl_easy_perform(curl);  
  189.         char tocode[64] = "UTF-8";  
  190.         if (CURLE_OK == res) {  
  191.             char *ct;  
  192.             res = curl_easy_getinfo(curl, CURLINFO_CONTENT_TYPE, &ct);  
  193.             if ((CURLE_OK == res) && ct)  
  194.                 printf("We received Content-Type: %s\n", ct);  
  195.             //printf("====\n");  
  196.             //int pos=strcspn(ct,"UTF-8");   
  197.             int index = indexOf(ct, "=");  
  198.             char* arr[3];  
  199.             if (index > 0) {  
  200.                 split(arr, ct, "=");  
  201.                 //printf("%s\n", arr[1]);  
  202.                 strcpy(tocode, arr[1]);  
  203.             }  
  204.             FILE *fp = NULL;  
  205.             fp = fopen(TMP_FILE, "r");  
  206.             char src_html[HTML_BUFFER_SIZE]={0};  
  207.             char output_html[HTML_BUFFER_SIZE]={0};  
  208.             char tmp_html[HTML_BUFFER_SIZE]={0};  
  209.             if (fp) {  
  210.                 fread(src_html, HTML_BUFFER_SIZE, 1, fp);     
  211.                 strcpy(tmp_html,src_html);  
  212.                 if(index <0) {  
  213.                     GetCharset(tmp_html,tocode);  
  214.                     printf("%s\n",tocode);  
  215.                 }  
  216.                 int iRet;  
  217.                 //打开字符集转换    
  218.                 iconv_t hIconv = iconv_open(tocode,"iso-8859-1");  
  219.                 if (-1 == (int) hIconv) {  
  220.                     return -1; //打开失败,可能不支持的字符集    
  221.                 }       
  222.                 printf("%s\n",src_html);  
  223.                 //开始转换    
  224.                 iRet = iconv(hIconv, (char**) (&src_html), strlen(src_html), (char**) (&output_html), strlen(src_html));                  
  225.                 printf("%s\n", output_html);    
  226.                 printf("ok");  
  227.                 if(strcmp(output_html,"")==0)  
  228.                 {  
  229.                      printf("%s\n",src_html);  
  230.                 }  
  231.                 //关闭字符集转换    
  232.                 iconv_close(hIconv);  
  233.                    
  234.             }  
  235.   
  236.         }  
  237.         curl_easy_cleanup(curl);  
  238.     }  
  239. }  
  240.   
  241. int main(int argc, char* argv) {  
  242.     //printf("%s\n",argv[1]);  
  243.     //http://192.168.1.6:8080/TestServer/index.html?fdasf=123456&af=89  
  244.     test_post("http://192.168.1.6:8080/TestServer/index.html","wd=hava&hehe=123456");  
  245.     //test_get("http://www.baidu.com/");  
  246.     printf("\nok");  
  247.     return 0;  
  248. }  

你可能感兴趣的:(实现HTTP协议Get、Post和文件上传功能——使用libcurl接口实现)