zoukankan      html  css  js  c++  java
  • Windows 和 Linux下使用socket下载网页页面内容(可设置接收/发送超时)的代码

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

    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 != '')  
    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 == '' || *s2 == '')  
    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," ");  
    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 "  
    126.                 "Host:%s:%u "  
    127.                 "Content-Type: application/x-www-form-urlencoded "  
    128.                 "Keep-Alive: Keep-Alive: timeout=%d "  
    129.                 "Connection: keep-alive "  
    130.                 "Accept:text/html "  
    131.                 "Authorization: Basic %s "  
    132.                 " "  
    133.                 ,url,ext_param,webserverip,httpport,net_timeout,auth  
    134.                 );  
    135.     }  
    136.     //GET  
    137.     return sprintf(header,  
    138.             "GET /%s HTTP/1.1 "  
    139.             "Host:%s:%u "  
    140.             "Content-Type: application/x-www-form-urlencoded "  
    141.             "Keep-Alive: timeout=%d "  
    142.             "Connection: keep-alive "  
    143.             "Accept:text/html "  
    144.             "Authorization: Basic %s "  
    145.             " "  
    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 ");  必有字段 
    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 " 
    166.             "Host:%s " 
    167.             "Content-Type:application/x-www-form-urlencoded " 
    168.             "Accept:text/html " 
    169.             " " 
    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 == ' ')  
    192.             {  
    193.                 if(strncasecmp(p," ",4) != 0)//http头没有结束  
    194.                 {  
    195.                     p+=2;//过滤   
    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 == ' ')  
    224.                 {  
    225.                     if(strncmp(p," ",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 ",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 ",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 ",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 ",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 ",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 ",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 ",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 http头: %s ",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("内存分配失败 ");  
    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 ",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  ",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 ",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," ");  
    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 ",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("分析服务器返回内容失败.返回内容为: %s ",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. }  
  • 相关阅读:
    Unix命令大全
    vs2008 与 IE8出现的兼容性问题
    Java 创建文件、文件夹以及临时文件
    如何修改Wamp中mysql默认空密码
    PAT 乙级真题 1003.数素数
    Tags support in htmlText flash as3
    DelphiXE4 FireMonkey 试玩记录,开发IOS应用 还是移植
    10 Great iphone App Review sites to Promote your Apps!
    HTML tags in textfield
    Delphi XE4 IOS 开发, "No eligible applications were found“
  • 原文地址:https://www.cnblogs.com/lidabo/p/3804238.html
Copyright © 2011-2022 走看看