linuxc语⾔实现下载⽂件,sockethttp⽂件下载器c语⾔实现socket真是⼀个神奇的东西,可以说是另外⼀扇⼤门, 蕴藏着很多有趣的东西, 本⽂就介绍如何使⽤c语⾔使⽤socket实现⼀个http⽂件下载器.
下载分为以下⼏个过程
解析出下载地址中的域名和⽂件名
通过域名获取服务器的IP地址
与⽬标服务器建⽴连接
构建http请求头并将其发送到服务器
等待服务器响应然后接收响应头
解析响应头, 判断返回码, 分离开响应头, 并且响应的正⽂内容以字节形式写⼊⽂件, 正⽂内容与头部⽤两个\n\r分开
具体实现完全可以通过代码看明⽩, 只需要看main函数和download函数即可, 其他函数都不是核⼼
完整的实现(Linux平台)
#include
#include
chrome直接下载#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
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 *domain, int *port, char *file_name)
/*通过url解析出域名, 端⼝, 以及⽂件名*/
int j = 0;
int start = 0;
*port = 80;
char *patterns[] = {"", "", NULL};
for (int i = 0; patterns[i]; i++)
if (strncmp(url, patterns[i], strlen(patterns[i])) == 0) start = strlen(patterns[i]);
//解析域名, 这⾥处理时域名后⾯的端⼝号会保留
for (int i = start; url[i] != '/' && url[i] != '\0'; i++, j++) domain[j] = url[i];
domain[j] = '\0';
//解析端⼝号, 如果没有, 那么设置端⼝为80
char *pos = strstr(domain, ":");
if (pos)
sscanf(pos, ":%d", port);
//删除域名端⼝号
for (int i = 0; i < (int)strlen(domain); i++)
{
if (domain[i] == ':')
{
domain[i] = '\0';
break;
}
}
/
/获取下载⽂件名
j = 0;
for (int i = start; url[i] != '\0'; i++)
{
if (url[i] == '/')
{
if (i != strlen(url) - 1)
j = 0;
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", t_type);
pos = strstr(response, "Content-Length:");//内容的长度(字节)
if (pos)
sscanf(pos, "%*s %ld", &t_length);
return resp;
}
void get_ip_addr(char *domain, char *ip_addr)
{
/*通过域名得到相应的ip地址*/
struct hostent *host = gethostbyname(domain);
if (!host)
{
ip_addr = NULL;
return;
}
for (int i = 0; host->h_addr_list[i]; i++)
{
strcpy(ip_addr, inet_ntoa( * (struct in_addr*) host->h_addr_list[i]));
void progressBar(long cur_size, long total_size)
{
/*⽤于显⽰下载进度条*/
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);
printf("\r%.2f%%\t[%-*.*s] %.2f/%.2fMB", percent * 100, numTotal, numShow, sign, cur_size / 1024.0 / 1024.0, total_size / 1024.0 / 1024.0);
fflush(stdout);
if (numShow == numTotal)
printf("\n");
}
void * download(void * socket_d)
{
/*下载⽂件函数, 放在线程中执⾏*/
int client_socket = *(int *) socket_d;
int length = 0;
int mem_size = 4096;//mem_size might be enlarge, so reset it
int buf_len = mem_size;//read 4k each time
int len;
//创建⽂件描述符
int fd = open(resp.file_name, O_CREAT | O_WRONLY, S_IRWXG | S_IRWXO | S_IRWXU);
if (fd < 0)
{
printf("Create file failed\n");
char *buf = (char *) malloc(mem_size * sizeof(char));
//从套接字中读取⽂件流
while ((len = read(client_socket, buf, buf_len)) != 0 && length < t_length) {
write(fd, buf, len);
length += len;
progressBar(length, t_length);
}
if (length == t_length)
printf("Download successful ^_^\n\n");
}
int main(int argc, char const *argv[])
{
char url[2048] = "127.0.0.1";
char domain[64] = {0};
char ip_addr[16] = {0};
int port = 80;
char file_name[256] = {0};
if (argc == 1)
{
printf("Input a valid URL please\n");
exit(0);
}
else
strcpy(url, argv[1]);
puts("1: ");
parse_url(url, domain, &port, file_name);
if (argc == 3)
strcpy(file_name, argv[2]);
puts("2: Get ");
get_ip_addr(domain, ip_addr);
if (strlen(ip_addr) == 0)