#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);
}
openssl 实现https下载
最新推荐文章于 2024-07-09 10:16:26 发布