• 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. }  
  • 相关阅读:
    转 windows查看端口占用命令
    servlet 让浏览器输出中文,并成功打印出来.2种方法
    ctrl+shift+i eclipse快捷键,debug时显示全黑屏
    转 一台电脑安装多个tomcat
    如何从windows中拷贝文件到linux (ubuntu)??
    Eclipse Java注释模板设置简介,更改字体大小
    sikuli 如何 清空文本框中的内容??解决方法!
    servlet 中通过response下载文件
    servlet乱码 解决方法 2种方法
    关于JAVA路径 问题
  • 原文地址:https://www.cnblogs.com/lidabo/p/3804238.html
Copyright © 2020-2023  润新知