openssl 实现https下载

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <time.h>
#include <errno.h>
#include <sys/socket.h>
//#include <sys/types.h>
//#include <resolv.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <assert.h>
#include <netdb.h>

#include <openssl/ssl.h>
#include <openssl/err.h>

struct resp_header//保持相应头信息
{
    int status_code;//HTTP/1.1 '200' OK
    char content_type[128];//Content-Type: application/gzip
    long content_length;//Content-Length: 11683079
    char file_name[256];
};
struct resp_header resp;//全剧变量以便在多个进程中使用

void parse_url(const char* url, char* file_name)
{
    /*通过url解析出域名, 端口, 以及文件名*/
    int j = 0, i = 0;
    int start = 0;
    char* patterns[] = { "http://", "https://", NULL };

    for (i = 0; patterns[i]; i++)
        if (strncmp(url, patterns[i], strlen(patterns[i])) == 0)
            start = strlen(patterns[i]);

    //获取下载文件名
    j = 0;
    for (i = start; url[i] != '\0'; i++)
    {
        if (url[i] == '/')
        {
            if (i != strlen(url) - 1)
                j = 0;
            continue;
        }
        else
            file_name[j++] = url[i];
    }
    file_name[j] = '\0';
}

struct resp_header get_resp_header(const char* response)
{
    /*获取响应头的信息*/
    struct resp_header resp;

    char* pos = strstr(response, "HTTP/");
    if (pos)
        sscanf(pos, "%*s %d", &resp.status_code);//返回状态码

    pos = strstr(response, "Content-Type:");//返回内容类型
    if (pos)
        sscanf(pos, "%*s %s", resp.content_type);

    pos = strstr(response, "Content-Length:");//内容的长度(字节)
    if (pos)
        sscanf(pos, "%*s %ld", &resp.content_length);

    return resp;
}

void progressBar(long cur_size, long total_size)
{
    char curPercent[10] = { 0x00 };
    char filesize[10] = { 0x00 };
    /*用于显示下载进度条*/
    float percent = (float)cur_size / total_size;
    const int numTotal = 50;
    int numShow = (int)(numTotal * percent);

    if (numShow == 0)
        numShow = 1;

    if (numShow > numTotal)
        numShow = numTotal;

    char sign[51] = { 0 };
    memset(sign, '=', numTotal);

    fflush(stdout);
    //logger_log(LOG_DEBUG,logger_log(LOG_DEBUG, "%.2f%%[%-*.*s] %.2f/%.2fMB", percent * 100, numTotal, numShow, sign, cur_size / 1024.0 / 1024.0, total_size / 1024.0 / 1024.0);
    if (total_size > 1024 * 1024)
    {
        sprintf(filesize, "%.2f/%.2fMB", cur_size / 1024.0 / 1024.0, total_size / 1024.0 / 1024.0);
        //logger_log(LOG_DEBUG, "%.2f%%\t %.2f/%.2fMB", percent * 100, cur_size / 1024.0 / 1024.0, total_size / 1024.0 / 1024.0);
    }
    else if (total_size > 1024)
    {
        sprintf(filesize, "%.2f/%.2fK", cur_size / 1024.0, total_size / 1024.0);
        //logger_log(LOG_DEBUG, "%.2f%%\t %.2f/%.2fK", percent * 100, cur_size / 1024.0, total_size / 1024.0);
    }
    else
    {
        sprintf(filesize, "%.2f/%.2fdbyte", cur_size, total_size);
       // logger_log(LOG_DEBUG, "%.2f%%\t %d/%dbyte", percent * 100, cur_size, total_size);
    }	//fflush(stdout);
    sprintf(curPercent, "%.2f%%", percent * 100);
    msgSent(sockCmd, curPercent, filesize, MSGBOX_DBUPDATEPRO);
    if (numShow == numTotal)
    {
        if (total_size > 1024 * 1024)
            logger_log(LOG_DEBUG, "%.2f%%\t %.2f/%.2fMB", percent * 100, cur_size / 1024.0 / 1024.0, total_size / 1024.0 / 1024.0);
        else if (total_size > 1024)
            logger_log(LOG_DEBUG, "%.2f%%\t %.2f/%.2fK", percent * 100, cur_size / 1024.0, total_size / 1024.0);
        else
            logger_log(LOG_DEBUG, "%.2f%%\t %d/%dbyte", percent * 100, cur_size, total_size);
    }
}

void download(const char* buf, char *path) {
    if (!buf) {
        return;
    }
    printf("%s\n", buf);
    char file_name[256] = { 0 };
    char host[64] = { 0 };
    char url[128] = { 0 };
    char ip[16] = { 0 };
    char* ret;
    int buf_len = strlen(buf);
    ret = strstr(buf, "https");
    if (!ret) {
        fprintf(stderr, "need https");
        return;
    }
    parse_url(buf, file_name);
    //最后以‘/’结束
    strcat(path, file_name);
    ret = strstr(buf, "//");
    if (!ret) {
        fprintf(stderr, "error //");
        return;
    }
    char* end = strstr(ret + 2, "/");
    if (!end) {
        fprintf(stderr, "error /");
        return;
    }

    assert(sizeof(host) > end - ret - 2);
    strncpy(host, ret + 2, end - ret - 2);

    struct hostent* hostinfo = gethostbyname(host);
    if (!hostinfo) {
        fprintf(stderr, "hostinfo /");
        return;
    }
    //memcpy(ip, hostinfo->h_addr, 4);//32位ip地址=4字节  structaddr
    strcpy(ip, inet_ntoa(*(struct in_addr*)hostinfo->h_addr_list[0]));

    assert(sizeof(url) > strlen(end + 1));
    strcpy(url, end + 1);

    int sockfd, len;
    struct sockaddr_in dest;
    char buffer[1024];
    SSL_CTX* ctx;
    SSL* ssl;
    int port = 443;

    /* SSL 库初始化 */
    SSL_library_init();
    OpenSSL_add_all_algorithms();
    SSL_load_error_strings();
    ctx = SSL_CTX_new(SSLv23_client_method());
    if (ctx == NULL) {
        ERR_print_errors_fp(stdout);
        exit(1);
    }
    if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
        perror("Socket Create Fail!");
        exit(errno);
    }
    /* 建立 TCP 连接 */
    bzero(&dest, sizeof(dest));
    dest.sin_family = AF_INET;
    dest.sin_port = htons(port);
    if (inet_aton(ip, (struct in_addr*)&dest.sin_addr.s_addr) == 0) {
        perror("Socket Init Fail!");
        exit(errno);
    }
    if (connect(sockfd, (struct sockaddr*)&dest, sizeof(dest)) != 0) {
        perror("Socket Connect Fail!");
        exit(errno);
    }
    printf("%s connect ok\n", ip);

    /* 绑定 Socket 与 SSL */
    ssl = SSL_new(ctx);
    SSL_set_fd(ssl, sockfd);
    /* 建立 SSL 连接 */
    if (SSL_connect(ssl) == -1) {
        ERR_print_errors_fp(stderr);
        return;
    }
    else {
        printf("SSL Connected with %s encryption\n", SSL_get_cipher(ssl));
    }

    sprintf(buffer, "GET /%s HTTP/1.1\r\nHost: %s\r\nConnection: Close\r\n\r\n", url, host);
    /* SSL 发数据 */
    len = SSL_write(ssl, buffer, strlen(buffer));
    if (len < 0) {
        printf("SSL Send failure! errno = %d, err_msg = %s\n", errno, strerror(errno));
        return;
    }

    bzero(buffer, 1024);
    int mem_size = 4096;
    int length = 0;
    char* databuf = (char*)malloc(mem_size * sizeof(char));
    char* response = (char*)malloc(mem_size * sizeof(char));

    //每次单个字符读取响应头信息, 仅仅读取的是响应部分的头部, 后面单独开线程下载
    while ((len = SSL_read(ssl, databuf, 1)) != 0)
    {
        if (length + len > mem_size)
        {
            //动态内存申请, 因为无法确定响应头内容长度
            mem_size *= 2;
            char* temp = (char*)realloc(response, sizeof(char) * mem_size);
            if (temp == NULL)
            {
                printf("realloc failed\n");
                exit(-1);
            }
            response = temp;
        }

        databuf[len] = '\0';
        strcat(response, databuf);

        //找到响应头的头部信息, 两个"\n\r"为分割点
        int flag = 0;
        int i = 0;
        for (i = strlen(response) - 1; response[i] == '\n' || response[i] == '\r'; i--, flag++);
        if (flag == 4)
            break;

        length += len;
    }

    //printf("\n>>>>Response header:<<<<\n%s", response);

    if (!strstr(response, "200 OK")) {
        fprintf(stderr, "response error");
        return;
    }
    resp = get_resp_header(response);
    FILE* fp = fopen(path, "wb");
    assert(fp);
    int nbytes;
    char ch;
    //read body
    int body_len = 0;
    while ((nbytes = SSL_read(ssl, buffer, 1024)) > 0) {

        body_len += nbytes;
        fwrite(buffer, nbytes, 1, fp);
        progressBar(body_len, resp.content_length);
    }
    fclose(fp);

    /* 关闭连接 */
    SSL_shutdown(ssl);
    SSL_free(ssl);
    close(sockfd);
    SSL_CTX_free(ctx);
}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Ya土豆儿~

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值