今天在处理下载文件时,发现在两台服务器做双机环境下,备机需要同步主机配置文件,而这些配置文件存放在WEB目录中的某些目录,相对路径已知,发现用系统自带的wget工具下载配置文件时,必须要把文件完整下载过来后再去读配置文件,这样感觉有点不灵活,于是自己写了一个HTTP下载工具,这样在获取配置文件内容时比较灵活,可以无缝加载配置文件,而不需要重启服务,以下程序摘录自代码中的完整示例代码,示例代码没有做什么异常处理,大家如果使用的话可以把它更加完善一下,http_client.c全文如下:
/************************************************************** * Copyright (C) 2014-2014 All rights reserved. * @Version: 1.0 * @Created: 2014-10-18 19:31:30 * @Author: guozi - 411239339@qq.com * @Description: http客户端程序,用于向WEB服务器发起http请求,将返回 * 结果写入文件 * @History: **************************************************************/ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/socket.h> #include <errno.h> #include <unistd.h> #include <netinet/in.h> #include <limits.h> #include <netdb.h> #include <arpa/inet.h> #include <ctype.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> FILE *gFile; /** * @brief http_string_strchr 搜索字符串右边起的第一个匹配字符 * * @Param: s 原始字符串 * @Param: x 待匹配的字符 * * Returns: 索到即返回x对应的值,否则返回0 */ char* http_string_strchr(char* s, char x) { int i = strlen(s); if(!(*s)) { return 0; } while(s[i-1]) { if(strchr(s+(i-1), x)) { return (s+(i-1)); } else { i--; } } return 0; } /** * @brief http_string_lower 字符串转换为全小写 * * @Param: s 原始字符串 */ void http_string_lower(char* s) { char *p = s; while(*p && *p!='\0' ) { if(*p > 'A' && *p < 'Z') *p = *p + 32; p++; } } /** * @brief http_gethost_info 字符串src中分析出网站地址和端口,并得到用户要下载的文件 * * @Param: src 输入字符串 * @Param: web WEB地址 * @Param: file 需要下载的文件 * @Param: port WEB端口号,默认为80 */ void http_gethost_info(char* src, char* web, char* file, int* port) { char* pa; char* pb; memset(web, 0, sizeof(web)); memset(file, 0, sizeof(file)); *port = 0; if(!(*src)) { return; } pa = src; if(!strncmp(pa, "http://", strlen("http://"))) { pa = src+strlen("http://"); } else if(!strncmp(pa, "https://", strlen( "https://"))) { pa = src+strlen( "https://"); } pb = strchr(pa, '/'); if(pb) { memcpy(web, pa, strlen(pa)-strlen(pb)); if(pb+1) { memcpy(file, pb+1, strlen(pb)-1); file[strlen(pb)-1] = 0; } } else { memcpy(web, pa, strlen(pa)); } if(pb) { web[strlen(pa) - strlen(pb)] = 0; } else { web[strlen(pa)] = 0; } pa = strchr(web, ':'); if(pa) { *port = atoi(pa + 1); } else { *port = 80; } } /** * @brief http_open_file * * @Param: file_path * * Returns: */ FILE *http_open_file(char *file_path) { gFile = fopen(file_path,"a+"); return gFile; } /** * @brief http_file_exsits 判断文件是否存在 * * @Param: path 文件路径 * * Returns: 存在返回true,不存在返回false */ int http_file_exsits(const char *path) { struct stat statbuf; if(lstat(path, &statbuf) ==0) return S_ISREG(statbuf.st_mode) != 0;//判断文件是否为常规文件 return 0; } /** * @brief http_socket_init 初始化套接字 * * @Param: port * @Param: host_addr * * Returns: 返回套接字描述符 */ int http_socket_init(int port, char *host_addr) { struct sockaddr_in server_addr; struct hostent *host; int sockfd; if((host=gethostbyname(host_addr)) == NULL)/*取得主机IP地址*/ { fprintf(stderr, "Gethostname error, %s\n ", strerror(errno)); return -1; } /* 客户程序开始建立 sockfd描述符 */ if((sockfd=socket(AF_INET,SOCK_STREAM,0)) == -1)/*建立SOCKET连接*/ { fprintf(stderr, "Socket Error:%s\a\n ",strerror(errno)); return -1; } /* 客户程序填充服务端的资料 */ bzero(&server_addr,sizeof(server_addr)); server_addr.sin_family=AF_INET; server_addr.sin_port=htons(port); server_addr.sin_addr=*((struct in_addr*)host->h_addr); /* 客户程序发起连接请求 */ if(connect(sockfd, (struct sockaddr*)(&server_addr), sizeof(struct sockaddr)) == -1)/*连接网站*/ { fprintf(stderr, "Connect Error:%s\a\n ",strerror(errno)); return -1; } return sockfd; } /********************************************************************* *filename: httpclient.c *purpose: HTTP协议客户端程序,可以用来下载网页 *********************************************************************/ int main(int argc, char *argv[]) { int sockfd = 0; char buffer[1024] = ""; int port = 0; int nbytes = 0; char host_file[1024] = ""; char host_addr[256] = ""; char request[1024] = ""; int send = 0; int totalsend = 0; int i = 0; FILE *file_fd; char *pt; char psave[4096]; char *file_name; size_t index = 0; if(argc < 2) { fprintf(stderr, "Usage:%s [web-address] \n ",argv[0]); exit(1); } printf( "parameter.1 is: %s\n ", argv[1]); http_gethost_info(argv[1], host_addr, host_file, &port);/*分析网址、端口、文件名等*/ printf( "webhost:%s\n ", host_addr); printf( "hostfile:%s\n ", host_file); printf( "port:%d\n\n ", port); file_name = argv[2]; if(! file_name) file_name = host_file; sockfd = http_socket_init(port, host_addr); sprintf(request, "GET /%s HTTP/1.1\r\nAccept: */*\r\nAccept-Language: zh-cn\r\n" "User-Agent: Mozilla/4.0 (compatible; MSIE 5.01; Windows NT 5.0)\r\n" "Host: %s:%d\r\nConnection: Close\r\n\r\n ", host_file, host_addr, port); printf( "%s\n", request);/*准备request,将要发送给主机*/ /*取得真实的文件名*/ if(*host_file) { pt = http_string_strchr(host_file, '/'); } else { pt = 0; } /*发送http请求request*/ send = 0; totalsend = 0; nbytes=strlen(request); while(totalsend < nbytes) { send = write(sockfd, request+totalsend, nbytes-totalsend); if(send == -1) { printf( "send error!%s\n ", strerror(errno)); exit(0); } totalsend += send; printf("%d bytes send OK!\n ", totalsend); } /*请求结束,等待响应*/ /*将结果写回到文件*/ if(http_file_exsits(file_name) ) { remove(file_name); } file_fd = http_open_file(file_name); if(file_fd == NULL) { perror("Open file error!\n"); return -1; } i=0; /*连接成功了,接收http响应,每次处理4096个字节*/ memset(psave,0,4096); while((nbytes=read(sockfd,buffer,1))==1) { if(i < 4) { /*这里处理http头部*/ if(buffer[0] == '\r' || buffer[0] == '\n') { i++; } else { i = 0; } printf( "%c", buffer[0]);/*把http头信息打印在屏幕上*/ } else /*如果结尾部分不为\r\n\r\n则表示头接收完毕,下面是请求内容*/ { psave[index++] = buffer[0]; if(index > 4096) { fwrite(psave,4096,1,file_fd); fflush(file_fd); memset(psave,0,4096); index = 0; } } } /*将剩余的字符写入文件*/ if(index <= 4096) { fwrite(psave,index,1,file_fd); fflush(file_fd); } close(sockfd); fclose(file_fd); return 0; }
编译:gcc http_client.c -o http_client
运行:./http_client http://192.168.1.1/sql.conf
也可以这样运行./http_client 192.168.1.1/sql.conf /tmp/sql.conf 后面的参数为指定文件存储位置。
这样就会把远程服务器上的文件下载过来了。
- 微信扫码赞助
- 支付宝赞助
2017/05/31 08:56:15
感谢指正,写这个的目的当时只是为了练习,没有考虑到产品级别的代码
2017/05/29 23:01:21
续上一条
gethostbyname()确实不能跟随端口号,在作者的c程序中,没有去掉,
手动修改在代码调用gethostbyname()之前,把host_addr字符串的内容修改,去掉端口数据就可以下载了。
还有一点小问题:下载的MP3
文件播放的时候断断续续。
2017/05/29 22:32:32
http://210.38.1.143:9999/file.kuyinyun.com/group1/M00/52/54/rBBGdFO10gGAV2YSABeMNNnn7Sc042.mp3
解释失败,看起来好像是Gethostname 失败了,带端口的不能使用gethostbyname()函数吗,求解
2017/05/14 21:10:50
Hello there, simply changed into alert to your weblog through Google, and found that it’s truly informative.
I am going to watch out for brussels. I’ll be grateful in the event you continue this in future.
Lots of people shall be benefited from your writing. Cheers!
2015/02/12 22:48:30
写得不错,转载了 www.zixue7.com
2014/10/22 20:26:33
求在线客服源代码哟..谢谢
2014/10/19 09:31:28
太给力了,期待果子能够跟新更多使用的东西!