首页 > 代码库 > Windows 和 Linux下使用socket下载网页页面内容(可设置接收/发送超时)的代码

Windows 和 Linux下使用socket下载网页页面内容(可设置接收/发送超时)的代码

主要难点在于设置recv()与send()的超时时间,具体要注意的事项,请看代码注释部分,下面是代码:

 

[cpp] view plaincopyprint?
 
  1. #include <stdio.h>  
  2. #include <sys/types.h>  
  3. #include <stdlib.h>  
  4. #include <string.h>  
  5. #include <errno.h>  
  6. #include <string.h>  
  7.   
  8. #ifdef _WIN32   ///包含win socket相关头文件  
  9. #include <winsock.h>  
  10. #pragma comment(lib,"ws2_32.lib")  
  11. #else       ///包含linux socket相关头文件  
  12. #include <unistd.h>  
  13. #include <strings.h>  
  14. #include <netinet/in.h>  
  15. #include <sys/socket.h>  
  16. #include <arpa/inet.h>  
  17. #include <netdb.h>  
  18. #include <fcntl.h>  
  19. #include <stdint.h>  
  20. #endif  
  21.   
  22. #ifdef _WIN32  
  23. #ifdef __cplusplus  
  24. extern "C"{  
  25. #endif  
  26.   
  27. int strcasecmp(const char *s1, const char *s2)  
  28. {  
  29.     while ((*s1 != ‘\0‘)  
  30.         && (tolower(*(unsigned char *) s1) ==  
  31.         tolower(*(unsigned char *) s2)))   
  32.     {  
  33.         s1++;  
  34.         s2++;  
  35.     }  
  36.     return tolower(*(unsigned char *) s1) - tolower(*(unsigned char *) s2);  
  37. }  
  38. int strncasecmp(const char *s1, const char *s2, unsigned int n)  
  39. {  
  40.     if (n == 0)  
  41.         return 0;  
  42.     while ((n-- != 0)  
  43.         && (tolower(*(unsigned char *) s1) ==  
  44.         tolower(*(unsigned char *) s2))) {  
  45.             if (n == 0 || *s1 == ‘\0‘ || *s2 == ‘\0‘)  
  46.                 return 0;  
  47.             s1++;  
  48.             s2++;  
  49.     }  
  50.     return tolower(*(unsigned char *) s1) - tolower(*(unsigned char *) s2);  
  51. }  
  52. #ifdef __cplusplus  
  53. }  
  54. #endif  
  55.   
  56. #endif  
  57. /********************************** 
  58. *功能:Base64编码 
  59. *参数: 
  60.     src_data:待编码的字符串 
  61.     coded_data:编码后的字符串 
  62. *返回值:-1,失败;0,成功 
  63. ***********************************/  
  64. int base64encode(const char * src_data/*in,待编码的字符串*/,char * coded_data/*out,编码后的字符串*/)  
  65. {  
  66.     const char EncodeTable[]="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";  
  67.     int src_data_len = strlen(src_data);  
  68.     int i;  
  69.     int lineLength=0;  
  70.     int mod=src_data_len % 3;  
  71.     unsigned char tmp[4]={0};  
  72.     char buff[5]={0};  
  73.     for(i=0;i<(int)(src_data_len / 3);i++)  
  74.     {  
  75.         tmp[1] = *src_data++;  
  76.         tmp[2] = *src_data++;  
  77.         tmp[3] = *src_data++;  
  78.         sprintf(buff,"%c%c%c%c", EncodeTable[tmp[1] >> 2],EncodeTable[((tmp[1] << 4) | (tmp[2] >> 4)) & 0x3F],EncodeTable[((tmp[2] << 2) | (tmp[3] >> 6)) & 0x3F],EncodeTable[tmp[3] & 0x3F]);  
  79.         strcat(coded_data,buff);  
  80.         if(lineLength+=4,lineLength==76)   
  81.         {  
  82.             strcat(coded_data,"\r\n");  
  83.             lineLength=0;  
  84.         }  
  85.     }     
  86.     if(mod==1)  
  87.     {  
  88.         tmp[1] = *src_data++;  
  89.         sprintf(buff,"%c%c==",EncodeTable[(tmp[1] & 0xFC) >> 2],EncodeTable[((tmp[1] & 0x03) << 4)]);  
  90.         strcat(coded_data,buff);  
  91.     }  
  92.     else if(mod==2)  
  93.     {  
  94.         tmp[1] = *src_data++;  
  95.         tmp[2] = *src_data++;  
  96.         sprintf(buff,"%c%c%c=",EncodeTable[(tmp[1] & 0xFC) >> 2],EncodeTable[((tmp[1] & 0x03) << 4) | ((tmp[2] & 0xF0) >> 4)],EncodeTable[((tmp[2] & 0x0F) << 2)]);  
  97.         strcat(coded_data,buff);  
  98.     }  
  99.     return 0;  
  100. }  
  101.   
  102.   
  103. //格式化http头,返回值:-1失败,-2用户名或密码无效;>=0 成功  
  104. int format_http_header(const char * webserverip,  
  105.             unsigned short httpport/*web server 端口*/,  
  106.             const char * url/*页面相对url,下载的页面为:http://ip/url"*/,  
  107.             const char * username/*网站认证用户*/,  
  108.             const char * password/*认证密码*/,  
  109.             const char * ext_param/*访问网页附加参数*/,  
  110.             int net_timeout/*超时时间,秒*/,  
  111.             char header[512]/*out*/)  
  112. {  
  113.     int len =0;   
  114.     char buf_auth[100]={0},auth[100]={0};  
  115.     sprintf(buf_auth,"%s:%s",username,password);  
  116.     base64encode(buf_auth,auth);  
  117.     if(ext_param)   
  118.     {  
  119.         len = strlen(ext_param);  
  120.     }  
  121.     if(len)  
  122.     {  
  123.         //GET  
  124.         return sprintf(header,  
  125.                 "GET /%s?%s HTTP/1.1\r\n"  
  126.                 "Host:%s:%u\r\n"  
  127.                 "Content-Type: application/x-www-form-urlencoded\r\n"  
  128.                 "Keep-Alive: Keep-Alive: timeout=%d\r\n"  
  129.                 "Connection: keep-alive\r\n"  
  130.                 "Accept:text/html\r\n"  
  131.                 "Authorization: Basic %s\r\n"  
  132.                 "\r\n"  
  133.                 ,url,ext_param,webserverip,httpport,net_timeout,auth  
  134.                 );  
  135.     }  
  136.     //GET  
  137.     return sprintf(header,  
  138.             "GET /%s HTTP/1.1\r\n"  
  139.             "Host:%s:%u\r\n"  
  140.             "Content-Type: application/x-www-form-urlencoded\r\n"  
  141.             "Keep-Alive: timeout=%d\r\n"  
  142.             "Connection: keep-alive\r\n"  
  143.             "Accept:text/html\r\n"  
  144.             "Authorization: Basic %s\r\n"  
  145.             "\r\n"  
  146.             ,url,webserverip,httpport,net_timeout,auth  
  147.             );  
  148.     /*POST /login.php HTTP/1.1 必有字段 
  149.     Host: www.webserver.com:80 必有字段 
  150.     User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; zh-CN; rv:1.9) Gecko/2008052906 Firefox/3.0 
  151.     Accept: text/html,application/xhtml+xml,application/xml;q=0.9,**; q=.2\r\n");  必有字段 
  152.     Accept-Language: zh-cn,zh;q=0.5 
  153.     Accept-Encoding: gzip,deflate 
  154.     Accept-Charset: gb2312,utf-8;q=0.7,*;q=0.7 
  155.     Keep-Alive: 300 
  156.     Connection: keep-alive 
  157.     Referer: http://www.vckbase.com/ 
  158.     Cookie: ASPSESSIONIDCSAATTCD=DOMMILABJOPANJPNNAKAMCPK 
  159.     Content-Type: application/x-www-form-urlencoded 必有字段 
  160.     Content-Length: 79 post方式时必有字段*/  
  161.       
  162.   
  163.     /*GET方式HTTP头写法*/  
  164.     /*sprintf(header, 
  165.             "GET /ipnc/php/ipnc.php?%s HTTP/1.1\r\n" 
  166.             "Host:%s\r\n" 
  167.             "Content-Type:application/x-www-form-urlencoded\r\n" 
  168.             "Accept:text/html\r\n" 
  169.             "\r\n" 
  170.             ,parm,serverip 
  171.             );*/  
  172. }  
  173. int parse_response_http_header(const char * all_contents/*接收到的所有内容,包含http头*/,char ** contents/*返回自己需要的内容*/)  
  174. {  
  175.     /** 
  176.     *根据需求分析网页的内容 
  177.     **/  
  178.     return 0;  
  179. }  
  180. //分析返回的内容的长度  
  181. int parse_respose_contents_length(const char * header/*in,http头*/)  
  182. {  
  183.     char * p = (char *)header;  
  184.     int tmp = 0;  
  185. #if 1  
  186.     if(p)  
  187.     {  
  188.         //获取内容长度  
  189.         while(*p)  
  190.         {  
  191.             if(*p == ‘\r‘)  
  192.             {  
  193.                 if(strncasecmp(p,"\r\n\r\n",4) != 0)//http头没有结束  
  194.                 {  
  195.                     p+=2;//过滤\n  
  196.                     if(strncasecmp(p,"Content-Length",14) == 0)  
  197.                     {  
  198.                         while(*p)  
  199.                         {  
  200.                             if(*p == ‘:‘)  
  201.                             {  
  202.                                 p++;  
  203.                                 tmp = atoi(p);  
  204.                                 break;  
  205.                             }  
  206.                             p++;  
  207.                         }  
  208.                         break;  
  209.                     }  
  210.                 }  
  211.                 else  
  212.                 {  
  213.                     break;  
  214.                 }  
  215.             }  
  216.             p++;  
  217.         }  
  218.         if(!tmp)//没有Content-Length字段  
  219.         {  
  220.               
  221.             for(p = (char*)header;*p;p++)  
  222.             {  
  223.                 if(*p == ‘\r‘)  
  224.                 {  
  225.                     if(strncmp(p,"\r\n\r\n",4) == 0)  
  226.                     {  
  227.                         p+=4;  
  228.                         tmp = strlen(p);  
  229.                         break;  
  230.                     }  
  231.                 }  
  232.             }  
  233.         }  
  234.     }  
  235. #endif  
  236.     return tmp;  
  237. }  
  238.   
  239. #define HTTP_RECV_BUFFER_SIZE 1024*1024*3 //3MB的接收缓存  
  240. #define RECV_BUFF_SIZE  1024  
  241.   
  242. int download_web_page(const char * ipv4/*web server ip地址*/,  
  243.             unsigned short httpport/*web server 端口*/,  
  244.             const char * url/*页面相对url,下载的页面为:http://ip/url"*/,  
  245.             const char * username/*网站认证用户*/,  
  246.             const char * password/*认证密码*/,  
  247.             const char * ext_param/*访问网页附加参数*/,  
  248.             int net_timeout/*网络超时时间,秒*/,  
  249.             char ** contents/*out:返回的实际内容,无http头,需要使用free函数手动释放空间*/  
  250.             )  
  251. {  
  252. #ifdef _WIN32  
  253.     WSADATA wsaData;          //指向WinSocket信息结构的指针  
  254. #endif  
  255.     struct sockaddr_in server_addr;  
  256.     int sockfd = -1;  
  257.     char szHttpHeader[1024]={0};  
  258.     char * pszBuffer    =   NULL;///堆栈溢出,所以使用堆空间  
  259.     char szRecvBuffer[RECV_BUFF_SIZE+1]={0};  
  260.     int len = 0,total_recv_len=0,total_contents_len = 0,re=-1;  
  261.     unsigned long flags;  
  262.     fd_set fs;  
  263.     char * pHttpHeaderEnd = NULL;  
  264. #ifdef _WIN32  
  265.     /* 
  266.     *这里请注意 
  267.     *windows下设置接收/发送超时时间时,setsockopt函数对应的超时时间为int型(且超时时间的值的单位为毫秒,当时我直接填写为秒,老是接收超时) 
  268.     *linux下为struct timeval结构 
  269.     */  
  270.     int timeout = net_timeout*1000;  
  271.     struct timeval select_timeout={0};  
  272.     select_timeout.tv_sec=net_timeout;  
  273. #else  
  274.     struct timeval timeout={.tv_sec=net_timeout,.tv_usec=0};  
  275. #endif  
  276.   
  277. #ifdef _WIN32  
  278.     if(WSAStartup(MAKEWORD( 1, 1 ), &wsaData )!=0)//进行WinSocket的初始化  
  279.     {  
  280.         WSACleanup();  
  281.         return -1;//Can‘t initiates windows socket!初始化失败  
  282.     }  
  283. #endif  
  284.   
  285.     if((sockfd = socket(AF_INET,SOCK_STREAM,0)) <= 0)  
  286.     {  
  287. #if defined CONSOLE || defined LINUX  
  288.         printf("创建socket失败.错误代码:%d,错误原因:%s\n",errno,strerror(errno));  
  289. #endif  
  290.         return -1;//create socket fd failed  
  291.     }  
  292.     ///设置接收超时时间  
  293.     if(setsockopt(sockfd,SOL_SOCKET,SO_RCVTIMEO,(char *)&timeout,sizeof(timeout)) != 0)  
  294.     {  
  295. #if defined CONSOLE || defined LINUX  
  296.         printf("设置socket发送超时时间失败.错误代码:%d,错误原因:%s\n",errno,strerror(errno));  
  297. #endif  
  298. #ifdef _WIN32  
  299.         closesocket(sockfd);  
  300. #else  
  301.         close(sockfd);  
  302. #endif  
  303.         return -1;  
  304.     }  
  305.     ///设置发送超时时间  
  306.     if(setsockopt(sockfd,SOL_SOCKET,SO_SNDTIMEO,(char *)&timeout,sizeof(timeout)) != 0)  
  307.     {  
  308. #if defined CONSOLE || defined LINUX  
  309.         printf("设置socket接收超时时间失败.错误代码:%d,错误原因:%s\n",errno,strerror(errno));  
  310. #endif  
  311. #ifdef _WIN32  
  312.         closesocket(sockfd);  
  313. #else  
  314.         close(sockfd);  
  315. #endif  
  316.         return -1;  
  317.     }  
  318.     ///设置非阻塞方式,使用select来判断connect是否超时  
  319. #ifdef _WIN32  
  320.     flags=1;  
  321.     if( ioctlsocket(sockfd,FIONBIO,&flags) != 0)  
  322. #else  
  323.     flags=fcntl(sockfd,F_GETFL,0);  
  324.     flags |= O_NONBLOCK;  
  325.     if( fcntl(sockfd,F_SETFL,flags) != 0)  
  326. #endif  
  327.     {  
  328. #if defined CONSOLE || defined LINUX  
  329.         printf("设置socket为非阻塞失败.错误代码:%d,错误原因:%s\n",errno,strerror(errno));  
  330. #endif  
  331. #ifdef _WIN32  
  332.         closesocket(sockfd);  
  333. #else  
  334.         close(sockfd);  
  335. #endif  
  336.         return -1;  
  337.     }  
  338.     ///设置连接参数  
  339. #ifdef _WIN32  
  340.     memset(&server_addr,0,sizeof(struct sockaddr_in));  
  341. #else  
  342.     bzero(&server_addr,sizeof(struct sockaddr_in));  
  343. #endif  
  344.     server_addr.sin_family      = AF_INET;  
  345.     server_addr.sin_port        = htons(httpport);  
  346.     server_addr.sin_addr.s_addr = inet_addr(ipv4);  
  347.     ///连接服务器  
  348.     if( connect(sockfd,(struct sockaddr *)&server_addr,sizeof(struct sockaddr_in)) < 0)  
  349.     {  
  350.         int ret = 0;  
  351.         ///判断是否超时  
  352.         FD_ZERO(&fs);  
  353.         FD_SET(sockfd,&fs);  
  354. #ifdef _WIN32  
  355.         ret = select(sockfd+1,NULL,&fs,NULL,&select_timeout);  
  356. #else  
  357.         ret = select(sockfd+1,NULL,&fs,NULL,&timeout);  
  358. #endif  
  359.         if(ret == 0)//超时  
  360.         {  
  361. #if defined CONSOLE || defined LINUX  
  362.             printf("链接服务器超时.错误代码:%d,错误原因:%s\n",errno,strerror(errno));  
  363. #endif  
  364. #ifdef _WIN32  
  365.             closesocket(sockfd);  
  366. #else  
  367.             close(sockfd);  
  368. #endif  
  369.             return -1;//连接超时  
  370.         }  
  371.         else if(ret < 0)///错误  
  372.         {  
  373. #if defined CONSOLE || defined LINUX  
  374.             printf("链接服务器时发生错误.错误代码:%d,错误原因:%s\n",errno,strerror(errno));  
  375. #endif  
  376. #ifdef _WIN32  
  377.             closesocket(sockfd);  
  378. #else  
  379.             close(sockfd);  
  380. #endif  
  381.             return -1;  
  382.         }  
  383.     }  
  384.     ///设置为阻塞方式发送和接收数据  
  385. #ifdef _WIN32  
  386.     flags=0;  
  387.     if( ioctlsocket(sockfd,FIONBIO,&flags) != 0)  
  388. #else  
  389.     flags=fcntl(sockfd,F_GETFL,0);  
  390.     flags &= ~O_NONBLOCK;  
  391.     if( fcntl(sockfd,F_SETFL,flags) != 0)  
  392. #endif  
  393.     {  
  394. #if defined CONSOLE || defined LINUX  
  395.         printf("设置socket为阻塞失败.错误代码:%d,错误原因:%s\n",errno,strerror(errno));  
  396. #endif  
  397. #ifdef _WIN32  
  398.         closesocket(sockfd);  
  399. #else  
  400.         close(sockfd);  
  401. #endif  
  402.         return -1;//ioctlsocket() error  
  403.     }  
  404.     format_http_header(ipv4,httpport,url,username,password,ext_param,net_timeout,szHttpHeader);  
  405.   
  406.     len = strlen(szHttpHeader);  
  407.     ///发送http头  
  408.     if(send(sockfd,szHttpHeader,len,0) != len)  
  409.     {  
  410. #if defined CONSOLE || defined LINUX  
  411.         printf("发送http头失败.错误代码:%d,错误原因:%s\nhttp头:\n%s\n",errno,strerror(errno),szHttpHeader);  
  412. #endif  
  413. #ifdef _WIN32  
  414.         closesocket(sockfd);  
  415. #else  
  416.         close(sockfd);  
  417. #endif  
  418.         return -1;//发送数据失败  
  419.     }  
  420.     ///准备接收数据  
  421.     pszBuffer = (char *)malloc(HTTP_RECV_BUFFER_SIZE);  
  422.     if(!pszBuffer)  
  423.     {  
  424. #if defined CONSOLE || defined LINUX  
  425.         printf("内存分配失败\n");  
  426. #endif  
  427. #ifdef _WIN32  
  428.         closesocket(sockfd);  
  429. #else  
  430.         close(sockfd);  
  431. #endif  
  432.         return -1;//outof memory  
  433.     }  
  434.   
  435. #ifdef _WIN32  
  436.     memset(pszBuffer,0,HTTP_RECV_BUFFER_SIZE);  
  437. #else  
  438.     bzero(pszBuffer,HTTP_RECV_BUFFER_SIZE);  
  439. #endif  
  440.   
  441.     while(1)  
  442.     {  
  443. #ifdef _WIN32  
  444.         len = recv(sockfd,szRecvBuffer,RECV_BUFF_SIZE,0);  
  445. #else  
  446.         len = recv(sockfd,szRecvBuffer,RECV_BUFF_SIZE,MSG_WAITALL);  
  447. #endif  
  448.         if(len == 0)  
  449.         {  
  450. #if defined CONSOLE || defined LINUX  
  451.             printf("接收数据超时,超时时间:%d s\n",net_timeout);  
  452. #endif  
  453. #ifdef _WIN32  
  454.             closesocket(sockfd);  
  455. #else  
  456.             close(sockfd);  
  457. #endif  
  458.             free(pszBuffer);  
  459.             return -1;//接收数据超时  
  460.         }  
  461.         if(len < 0 )  
  462.         {  
  463. #if defined CONSOLE || defined LINUX  
  464.             printf("接收数据错误,recv返回值:%d \n",len);  
  465. #endif  
  466. #ifdef _WIN32  
  467.             closesocket(sockfd);  
  468. #else  
  469.             close(sockfd);  
  470. #endif  
  471.             free(pszBuffer);  
  472.             return -1;//timeout  
  473.         }  
  474.         //printf("%s",szBuffer);  
  475.         total_recv_len += len;  
  476.         if(total_recv_len > (HTTP_RECV_BUFFER_SIZE-1) )  
  477.         {  
  478. #if defined CONSOLE || defined LINUX  
  479.             printf("接收数据buffer空间不足,当前buffer大小:%d B\n",HTTP_RECV_BUFFER_SIZE-1);  
  480. #endif  
  481. #ifdef _WIN32  
  482.             closesocket(sockfd);  
  483. #else  
  484.             close(sockfd);  
  485. #endif  
  486.             free(pszBuffer);  
  487.             return -1;//not enough buffer size  
  488.         }  
  489.         strcat(pszBuffer,szRecvBuffer);  
  490.         if(len < RECV_BUFF_SIZE)  
  491.         {  
  492.             pHttpHeaderEnd = strstr(pszBuffer,"\r\n\r\n");  
  493.             if(pHttpHeaderEnd )  
  494.             {  
  495.                 if(!total_contents_len)///http返回头中标示的内容大小  
  496.                 {  
  497.                     total_contents_len = parse_respose_contents_length(pszBuffer);  
  498.                 }  
  499.                 pHttpHeaderEnd += 4;  
  500.                 //如果接收到的内容长度已经达到http返回头中标示的内容大小,停止接收  
  501.                 if( total_contents_len && strlen( pHttpHeaderEnd) >= total_contents_len )  
  502.                     break;  
  503.                 pHttpHeaderEnd = NULL;  
  504.             }             
  505.         }  
  506. #ifdef _WIN32  
  507.         memset(szRecvBuffer,0,sizeof(szRecvBuffer));  
  508. #else  
  509.         bzero(szRecvBuffer,sizeof(szRecvBuffer));  
  510. #endif  
  511.         len = 0;  
  512.     }  
  513.     if(strcmp(pszBuffer,"") == 0)  
  514.     {  
  515. #ifdef _WIN32  
  516.         closesocket(sockfd);  
  517. #else  
  518.         close(sockfd);  
  519. #endif  
  520.         free(pszBuffer);  
  521.         return -1;//recv data error  
  522.     }  
  523.     //printf("%s\n",szBuffer);  
  524.     * contents = NULL;  
  525.     re = parse_response_http_header(pszBuffer,contents);  
  526.     if( re != 0 || !(*contents))  
  527.     {  
  528.         if(*contents)  
  529.         {  
  530.             free(*contents);  
  531.         }  
  532. #if defined CONSOLE || defined LINUX  
  533.         printf("分析服务器返回内容失败.返回内容为:\n%s\n",pszBuffer);  
  534. #endif  
  535. #ifdef _WIN32  
  536.         closesocket(sockfd);  
  537. #else  
  538.         close(sockfd);  
  539. #endif  
  540.         free(pszBuffer);  
  541.         if( -401 == re)  
  542.             return -1;//用户名/密码无效  
  543.         return -1;//unknown error  
  544.     }  
  545. #ifdef _WIN32  
  546.     closesocket(sockfd);  
  547. #else  
  548.     close(sockfd);  
  549. #endif  
  550.     free(pszBuffer);  
  551.   
  552.   
  553. #ifdef _WIN32  
  554.     WSACleanup();  
  555. #endif  
  556.     return 0;  
  557. }