商务合作加Q:411239339

自己用C语言写的一个http下载工具

浏览:1377次阅读
7 条评论

共计 7173 个字符,预计需要花费 18 分钟才能阅读完成。

       今天在处理下载文件时,发现在两台服务器做双机环境下,备机需要同步主机配置文件,而这些配置文件存放在 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]   [file path]\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   后面的参数为指定文件存储位置。

这样就会把远程服务器上的文件下载过来了。

正文完
扫码赞助
post-qrcode
 0
果子
版权声明:本站原创文章,由 果子 于2014-10-18发表,共计7173字。
转载说明:除特殊说明外本站文章皆由果较瘦原创发布,转载请注明出处。
评论(7 条评论)
2017-05-31 08:56:15 回复

感谢指正,写这个的目的当时只是为了练习,没有考虑到产品级别的代码

 Linux  Chrome  中国湖南省长沙市电信
周东
2017-05-29 23:01:21 回复

续上一条
gethostbyname()确实不能跟随端口号,在作者的c程序中,没有去掉,
手动修改在代码调用gethostbyname()之前,把host_addr字符串的内容修改,去掉端口数据就可以下载了。
还有一点小问题:下载的MP3
文件播放的时候断断续续。

 Windows  Chrome  中国广东省广州市移动
周东
2017-05-29 22:32:32 回复

http://210.38.1.143:9999/file.kuyinyun.com/group1/M00/52/54/rBBGdFO10gGAV2YSABeMNNnn7Sc042.mp3
解释失败,看起来好像是Gethostname 失败了,带端口的不能使用gethostbyname()函数吗,求解

 Windows  Chrome  中国广东省广州市移动
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!

 Macintosh  Chrome  美国亚利桑那凤凰城
2015-02-12 22:48:30 回复

写得不错,转载了 http://www.zixue7.com

   Denglu  中国广东省广州市联通
lintao1984
2014-10-22 20:26:33 回复

求在线客服源代码哟..谢谢

   Denglu  中国山东省潍坊市电信
生活需要深度
2014-10-19 09:31:28 回复

太给力了,期待果子能够跟新更多使用的东西!

   Denglu  中国上海上海市科技网