基于HTTP的下载客户端

来源:互联网 发布:ubuntu如何下命令 编辑:程序博客网 时间:2024/05/22 16:04

这是我的综合项目概述。一个基于HTTP的下载客户端,下载的资源是QQ8.6客户端。


先说整体项目的框架以及各个模块的细节划分吧。


一个http下载客户端,划分功能模块,可以分成五个模块:


1、客户端解析浏览器传递过来的url,得到域名、端口、资源


2、客户端建立TCP_Socket连接


3、客户端向服务器发送下载QQ客户端请求


4、客户端接收到服务器的请求并且把QQ客户端写进文件


学习C语言时,最深刻的体会到,函数是作为整体工程中承上启下的作用,也就是每个模块之间,输入的参数,返回的是返回值,以此串联每一个模块,这就是过程化的编程。即使接触了面向对象的C++,还是认为,面向对象也是基于面向过程而进行的一个封装,并且凭空的面向对象。


程序代码本身是为了去解决生活中的问题,无论面向过程还是面向对象,实质都是现实问题的映射。


第一个模块传入一个url:
http://dldir1.qq.com/qqfile/qq/QQ8.6/18804/QQ8.6.exe,以及反向更新url里面的的各个模块(域名+端口+资源名字)。在解析url的时候,用到了几个不常用的函数strncmp和snprintf,设计了一个算法挑出了域名+资源,端口解析我直接默认了80。最后,打印url、source、hostname、source_path、port这五个作为调试。
承上启下,返回第二模块需要的域名和端口。


第二个,网络编程的基础知识,客户端发送服务器一个连接。需要的是IP地址+端口,这里没有IP怎么办,有域名,域名转换成IP,有了输入参数域名+端口,那么返回的就是一个传给下一个函数的socket。这个过程是LinuxC编程的基础知识,客户端与服务端进行连接的过程。


第三个,客户端向服务器发送请求,建立了tcp连接以后,转换为Linux C系统编程中对于文件的操作,所以,第一个参数,socketfd,发送请求,需要域名+端口+资源。这个模块的核心在于将请求+首部字段+其他一起打包到一个buf里面一次性发给服务器。


第四个,客户端接受服务器的请求,并且进行QQ客户端的下载。接受请求分为几个部分,接受请求报文中的状态码,判断是否200,其次得到报文主体的长度,然后取报文主体长的数据,取报文的时候用到了一个get_line的函数,这是tinny_httpd这个开源服务器的一行读取函数。而后socketfd作为读写文件描述符,正是Linux C编程里面一切皆文件的思想。客户端得到了状态码,资源长度,然后打开一个文件,接受服务端发过来的数据,然后写进文件。

C语言部分其实可以归纳三个关键字:基本语法+内存空间+承上启下的函数。而系统编程部分,在我看来,两个关键字:CPU核心调度+一切皆文件。整个小项目综合了很多Linux下的知识,但是以后的路还是很长,学习的是提炼的思想,而后还需要自己不但学习各种陌生的类库,各种新鲜的技术。


/***************************************************************************************Author: wenzhiyiEmail:447502390@qq.comDate:2016.09.24Project:HTTP_Download Project*****************************************************************************************/#include <stdio.h>#include <string.h>#include <stdlib.h>#include <unistd.h>#include "parser.h"#include "socket.h"#include "send_http.h"#include "recv_http.h"int http_download(const char *url){char hostname[128];unsigned int port;char source[256];char source_path[256];int socketfd;int ret;int length;//出错处理,判断输入的url是否合法if(url==NULL){    printf("input url is error!");    return -1;}//Module 1: Parser urlret=parser_url(url,hostname,&port,source_path);if(ret<0){    printf("parser_url is error!\n");    return -1;}//printf the informationprintf("hostname:%s\n",hostname);printf("port:%d\n",port);printf("source_path:%s\n",source_path);//Module2:Create tcp_socket socketfd=tcp_socket(hostname,port);if(ret<0){    printf("tcp_socket is error!\n");    return -1;}//Module3:Send_http_requestret=send_http_request(socketfd,source,source_path,hostname,port);if(ret<0){    printf("send_http_request is failed!\n");    return -1;}//Module4:Recv_http_requestret=recv_http_respond(socketfd,source_path,source);if(ret<0){   printf("recv_http_request is failed!\n");   return -1;}return 0;}int main(int argc,char **argv){int ret_http_download;//This is QQ download url from QQ char url[256]="http://dldir1.qq.com/qqfile/qq/QQ8.6/18804/QQ8.6.exe";ret_http_download=http_download(url);if(ret_http_download<0){    printf("http_download is error!\n");    return -1;}//when you see the printf , it is success to http_download printf("It is successfully to download!\n");return 0;}int parser_url(const char *url,char *hostname,unsigned int *port,char *source_path){int ret;const char *tmp1;const char *tmp2;ret=strncmp(url,"http://",7);if(ret!=0){   printf("strncmp is error!\n");   return -1;}tmp1=url+7;tmp2=strstr(tmp1,"/");snprintf(hostname,tmp2-tmp1+1,"%s",tmp1);*port=80; // default port is 80//parser source_path to array of source_pathsnprintf(source_path,strlen(tmp2)+1,"%s",tmp2);return 0;}int tcp_socket(const char *hostname,unsigned int port){int socketfd;struct sockaddr_in server_addr;int connect_ret;struct hostent *tmp_ip;socketfd=socket(AF_INET,SOCK_STREAM,0);if(socketfd<0){    printf("Create socket is failed!\n");    return -1;}//set sockaddr_in memset((struct sockaddr*)&server_addr,0,sizeof(server_addr));server_addr.sin_family=AF_INET;server_addr.sin_port=htons(port);tmp_ip=gethostbyname(hostname);if(tmp_ip==NULL){    printf("hostname is error!\n");    return -1;}memcpy(&server_addr.sin_addr.s_addr,(struct in_addr*)tmp_ip->h_addr,4);//connect to serverconnect_ret=connect(socketfd,(struct sockaddr*)&server_addr,sizeof(server_addr));if(connect_ret<0){    printf("Connect is failed!\n");    return -1;}printf("Connect is success!\n");printf("The socketfd is %d\n",socketfd);//return socketfd to send http_requestreturn socketfd;}int send_http_request (int socketfd,const char *source,const char *source_path,const char *hostname,unsigned int port){//malloc 申请一块内存char *request_buf=(char *)malloc(1024); bzero(request_buf,1024);//起始行strcat(request_buf,"GET ");strcat(request_buf,source_path);strcat(request_buf," HTTP/1.1\r\n");//首部字段 Hostchar buf[128];sprintf(buf,"%s:%d",hostname,port);strcat(request_buf,"Host: ");strcat(request_buf,buf);strcat(request_buf,"\r\n");//首部字段 aplikey//strcat(request_buf,"apikey: ");//strcat(request_buf,"1d500726c2666742c63209379c91f5d9");//strcat(request_buf,"\r\n");//首部字段 Acceptstrcat(request_buf,"Accept: ");strcat(request_buf,"*/*");strcat(request_buf,"\r\n");//首部字段 User-Agentstrcat(request_buf,"User-Agent: ");strcat(request_buf,"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/49.0.2623.75 Safari/537.36");strcat(request_buf,"\r\n");//首部字段 Connectstrcat(request_buf,"Connection: ");strcat(request_buf,"close");strcat(request_buf,"\r\n");//In the end of request_buf ,we add "\r\n"strcat(request_buf,"\r\n");int ret=send_request(socketfd,request_buf);if(ret<0){printf("send_http_request is failed\n");return -1;}printf("Request_buf is:%s",request_buf);//释放内存free(request_buf);return 0;}int send_request(int socketfd,const char *request_buf){int request_len=strlen(request_buf);int ret=0;int sum = 0;if(NULL==request_buf){printf("request_buf is NULL\n");return -1;}//circulate to send , until send all of request_bufwhile(sum<request_len){ret=send(socketfd,request_buf+sum,request_len-sum,0);if(ret<0){printf("send_request is failed!\n");return -1;}sum=ret+sum;}return 0;}int recv_http_respond(int socketfd,const char *source_path, char *source){int status_code;int length;int ret;//get the code for http_statusstatus_code = get_http_status(socketfd);if(status_code!=200){printf("get_http_status failed!\n");return -1;}//get the length for http_lengthlength = get_http_length(socketfd);if(length < 0){printf("get_http_length failed!\n");return -1;}//get source for http_serverret = get_source(source_path,source);if(ret < 0){printf("get_source failed!\n");return -1;}//download_source for http_serverret = download_source(socketfd,source,length);if(ret < 0){printf("download_source failed!\n");return -1;}return length;}int get_http_status(int socketfd){char buf[256];int status_code;//The function of get_line is define afterget_line(socketfd,buf,sizeof(buf));if(strncmp(buf,"HTTP/1.1",8)!=0){    printf("http respond header is notfound HTTP/1.1\n");    return -1;}//After nine size from the point to HTTP , we can find the address for the status_code;status_code=atoi(buf+9);return status_code;}// Refer to Tinny_Httpd,This is the function to get a line for respondint get_line(int socketfd, char *buf, int size){    int i = 0;    char c = '\0';    int n;    while ((i < size - 1) && (c != '\n'))    {            n = recv(socketfd, &c, 1, 0);        /* DEBUG printf("%02X\n", c); */        if (n > 0){            if (c == '\r')    {                n = recv(socketfd, &c, 1, MSG_PEEK);                /* DEBUG printf("%02X\n", c); */                if ((n > 0) && (c == '\n'))                    recv(socketfd, &c, 1, 0);                else                    c = '\n';            }            buf[i] = c;            i++;      }else            c = '\n';    }    buf[i] = '\0';        return(i);}int get_http_length(int socketfd){char buf[256];int ret;unsigned int http_length;ret=read(socketfd,buf,sizeof(buf));while((ret>0)&&strcmp(buf,"\n")){    printf("%s\n",buf);    if(strncmp(buf,"Content-length:",15)==0)    http_length=atoi(buf+16);    ret=read(socketfd,buf,sizeof(buf));}return http_length;}int get_source(const char *source_path,char *source){char *tmp;tmp=strrchr(source_path,'/');sprintf(source,"%s",tmp+1);return 0;}// The function is to download QQ ,open a file ,and read the data from socket ,and write into fileint download_source(int socketfd,const char *source,int size){FILE *fp;int recv_ret=0;char buf[1024];int write_ret=0;int freq = 0;unsigned int count = 0;double per;fp = fopen(source,"wb");if(fp==NULL){printf("fopen is failed!\n");return -1;}//circulate to write and read ,make sure all of date will be write into the file while((recv_ret = recv(socketfd,buf,sizeof(buf),0)) != 0){if(recv_ret < 0){printf("download_file recv");return -1;}write_ret = fwrite(buf,1,recv_ret,fp);if(write_ret != recv_ret){printf("fwrite is failed!\n");return -1;}count=count+write_ret;if(freq > 10){per = (count*100.0) / size;printf("\rdownload:%u --- percent: %%%f",count,per);freq = 0;}freq++;}fclose(fp);close(socketfd);return 0;}

这是我在ubuntu14.04下的编译代码并且执行,该截图显示了QQ客户端的下载成功,这就是一个简单的基于HTTP的下载客户端小项目了哈哈哈!



0 0
原创粉丝点击