c 语言登录系统网站下载文件,C语言 HTTP 下载文件

C语言 HTTP协议下载文件,实现断点续传,socket通讯,目前只支持ip和port方式连接,有兴趣的读者可完善域名方式。

代码分为 http.c: 实现http协议下载文件 ,socket.c: 封装linux socket函数,移植时只需修改socket.c中的函数即可。

希望对大家有帮助,本人亲测可用!

http.c

//http.c

#include

#include

#include

#include

#include

#include

#include

#include "socket.h"

#include "http.h"

#define MAX_RECV_SIZE    1440//硬件单包最大的接收字节数

char g_host[URL_LEN];

char g_ip[URL_LEN+1];//ip/域名

char g_port[5+1];

char g_buf_send[4*1024];//发送数据暂存区

char g_buf_recv[10*1024];//接收数据暂存区

BreakPoint_ST g_break_point;

/*

功能:判断断点有效性,现在校验url是否一致

参数:

返回:

>0---------有效,已下载文件大小

-1----------无效

*/

int Get_Breakpoint_Available(BreakPoint_ST *breakpoint,char *url,char *file_crc)

{

//判断断点是否有效,后续加入文件校验码

if((memcmp(breakpoint->url,url,strlen(url))== 0)&&(breakpoint->recv_size== MAX_RECV_SIZE))

return breakpoint->download_size;

else

{

return -1;

}

}

/*

功能:判断要下载文件是否存在断点

参数:

filename---要下载的文件名

file_crc----服务器返回下载文件的校验码

返回:

0---------无断点

>0--------有断点,已下载文件大小

*/

int Get_Breakpoint(char *url,char *filename,char *file_crc)

{

char filename_bp[64];

int fd = -1;

int ret;

BreakPoint_ST break_point;

//断点文件名 filename+bp

sprintf(filename_bp,"%s.bp",filename);

//检测是否存在filename断点文件

fd = open(filename_bp,O_RDONLY,S_IRUSR|S_IWUSR);

if(fd == -1)

{

#ifdef DEBUG_HTTP

printf("no exsit %s\n",filename_bp);

#endif

return 0;

}

//存在断点

ret = read(fd,&break_point,sizeof(break_point));

if(ret != sizeof(break_point))

{

perror("ERR:Get_Breakpoint read");

exit(-1);

}

close(fd);

//判断断点是否有效

ret = Get_Breakpoint_Available(&break_point,url,file_crc);

if(ret > 0)

return ret;

else

{

printf("%s not available\n",filename_bp);

remove(filename);

remove(filename_bp);

return 0;

}

}

/*

功能:保存断点信息,文件名filename.bp

参数:

filename---要下载的文件名

file_crc----服务器返回下载文件的校验码

返回:

0---------成功

>0--------有断点,已下载文件大小

*/

int Save_Breakpoint(char *url,char *filename,int download_size,char *file_crc)

{

int fd;

BreakPoint_ST breakpoint;

char filename_bp[128];//断点信息文件名,包含路径

sprintf(filename_bp,"%s.bp",filename);

/* 创建目的文件 */

if((fd=open(filename_bp,O_WRONLY|O_CREAT,S_IRUSR|S_IWUSR))==-1)

{

fprintf(stderr,"Open %s Error:%s\n",filename_bp,strerror(errno));

exit(1);

}

memset(&breakpoint,0x0,sizeof(breakpoint));

strcpy(breakpoint.url,url);

//strcpy(breakpoint.crc,file_crc);

strcpy(breakpoint.filename,filename);

breakpoint.download_size = download_size;

breakpoint.recv_size= MAX_RECV_SIZE;

//xu tioa zheng wei fen ci xie ru

if(write(fd,&breakpoint,sizeof(breakpoint)) != sizeof(breakpoint))

{

perror("ERR:Save_Breakpoint");

exit(1);

}

close(fd);

return 0;

}

/*

功能:保存文件,追加写

参数:

返回:

0---------成功

*/

int Save_File(char *filebuf,int filelength,char *filename)

{

int fd;

/* 创建目的文件追加写 */

if((fd=open(filename,O_WRONLY|O_CREAT|O_APPEND,S_IRUSR|S_IWUSR))==-1)

{

fprintf(stderr,"Open %s Error:%s\n",filename,strerror(errno));

exit(1);

}

//xu tioa zheng wei fen ci xie ru

if(write(fd,filebuf,filelength) != filelength)

{

perror("ERR:Save_File");

exit(1);

}

close(fd);

return 0;

}

int HTTP_GetResponseCode(void)

{

}

/*

功能:读取http返回的协议实体主体长度

参数:

revbuf--------接收到的返回值

返回值:

>=0---------内容(实体主体)的长度

-1-----------数据返回错误

*/

int HTTP_GetRecvLength(char *revbuf)

{

char *p1 = NULL;

int HTTP_Body = 0;//内容体长度

int HTTP_Head = 0;//HTTP 协议头长度

HTTP_Body = HTTP_GetContentLength(revbuf);

if(HTTP_Body == -1)

return -1;

p1=strstr(revbuf,"\r\n\r\n");

if(p1==NULL)

return -1;

else

{

HTTP_Head = p1- revbuf +4;// 4是\r\n\r\n的长度

return HTTP_Body+HTTP_Head;

}

}

/*

功能:读取http返回的Content-Length长度

参数:

revbuf--------接收到的数据

返回值:

>=0---------Content-Length长度

-1-----------数据返回错误

*/

int HTTP_GetContentLength(char *revbuf)

{

char *p1 = NULL, *p2 = NULL;

int HTTP_Body = 0;//内容体长度

p1 = strstr(revbuf,"Content-Length");

if(p1 == NULL)

return -1;

else

{

p2 = p1+strlen("Content-Length")+ 2;

HTTP_Body = atoi(p2);

return HTTP_Body;

}

}

/*

功能:

参数:

sockfd--------接收到的返回值

返回值:

>0---------接收到长度

-1----------失败

=0---------服务端断开连接

注:内部接收缓冲10k

*/

int HTTP_Recv(int sockfd,char *buf_recv)

{

int ret;

int recvlen=0;

int downloadlen = 0;

//int contentlen=0;

char buf_recv_tmp[10*1024+1];

memset(buf_recv_tmp,0x0,sizeof(buf_recv_tmp));

while(1)

{

ret = Recv(sockfd,buf_recv_tmp+recvlen,sizeof(buf_recv_tmp)-1,0);

if(ret <= 0)//下载失败

{

perror("ERR:recv fail");

return ret;

}

if(recvlen == 0)

{

#ifdef DEBUG_HTTP_RECV

printf("recv len = %d\n", ret);

printf("recv = %s\n", buf_recv_tmp);

#endif

//获取需要下载长度;

downloadlen = HTTP_GetRecvLength(buf_recv_tmp);

#ifdef DEBUG_HTTP_RECV

printf("downloadlen = %d\n",downloadlen);

#endif

}

recvlen += ret;

#ifdef DEBUG_HTTP_RECV

printf("total recvlen = %d\n",recvlen);

#endif

if(downloadlen == recvlen)//下载完成

break;

}

memcpy(buf_recv,buf_recv_tmp,downloadlen);

return recvlen;

}

/*

功能:获取下载url中的文件名,最后一个/后的字符

参数:

返回值:

0-----------成功

-1----------失败

注:内部接收缓冲10k

*/

int HTTP_GetFileName(char *url,char *filename)

{

//提取url中最后一个/后的内容

int len;

int i;

len = strlen(url);

for(i=len-1;i>0;i--)

{

if(url[i] == '/')

break;

}

if(i == 0)//下载地址错误

{

printf("url not contain '/'\n");

return -1;

}

else

{

strcpy(filename,url+i+1);

#ifdef DEBUG_HTTP

printf("filename=%s\n",filename);

#endif

return 0;

}

}

/*

功能:获取下载url中的路径,第一个/后的字符

参数:

返回值:

0-----------成功

-1----------失败

注:url ex "http://host:port/path"

*/

int HTTP_GetPath(char *url,char *path)

{

char *p;

p = strstr(url,"http://");

if(p == NULL)

{

p = strchr(url,'/');

if(p == NULL)

return -1;

else

{

strcpy(path,p);

return 0;

}

}

else

{

p = strchr(url+strlen("http://"),'/');

if(p == NULL)

return -1;

else

{

strcpy(path,p);

return 0;

}

}

}

/*

功能:获取下载url中的ip和port,ip支持域名,端口默认为80

参数:

返回值:

1-----------域名式

2-----------ip port式

-1----------失败

注:url ex "http://host:port/path"

*/

int HTTP_Get_IP_PORT(char *url,char *ip,char *port)

{

char *p = NULL;

int offset = 0;

char DOMAIN_NAME[128];

p = strstr(url,"http://");

if(p == NULL)

{

offset = 0;

}

else

{

offset = strlen("http://");

}

p = strchr(url+offset,'/');

if(p == NULL)

{

printf("url:%s format error\n",url);

return -1;

}

else

{

memset(DOMAIN_NAME,0x0,sizeof(DOMAIN_NAME));

memcpy(DOMAIN_NAME,url+offset,(p-url-offset));

p = strchr(DOMAIN_NAME,':');

if(p == NULL)

{

strcpy(ip,DOMAIN_NAME);

strcpy(port,"80");

//printf("ip %p,port %p\n",ip,port);

#ifdef DEBUG_HTTP

printf("ip=%s,port=%s\n",ip,port);//debug info

#endif

return 1;

}

else

{

*p = '\0';

strcpy(ip,DOMAIN_NAME);

strcpy(port,p+1);

#ifdef DEBUG_HTTP

printf("ip=%s,port=%s\n",ip,port);//debug info

#endif

return 2;

}

return 0;

}

}

void Package_Url_Get_File(char *path, char *range)

{

char buf[64];

memset(g_buf_send,0x0,sizeof(g_buf_send));

sprintf(g_buf_send, "GET %s",path);

//HTTP/1.1\r\n 前面需要一个空格

strcat(g_buf_send," HTTP/1.1\r\n");

strcat(g_buf_send, "Host: ");

strcat(g_buf_send, g_host);

//strcat(g_buf_send, ":");

//strcat(g_buf_send, PORT);

sprintf(buf, "\r\nRange: bytes=%s",range);

strcat(g_buf_send,buf);

strcat(g_buf_send, "\r\nKeep-Alive: 200");

strcat(g_buf_send,"\r\nConnection: Keep-Alive\r\n\r\n");

}

int Package_Url_Get_FileSize(char *url)

{

memset(g_buf_send,0x0,sizeof(g_buf_send));

sprintf(g_buf_send, "HEAD %s",url);

//HTTP/1.1\r\n 前面需要一个空格

strcat(g_buf_send," HTTP/1.1\r\n");

strcat(g_buf_send, "Host: ");

strcat(g_buf_send, g_host);

//strcat(g_buf_send, ":");

//strcat(g_buf_send, PORT);

strcat(g_buf_send,"\r\nConnection: Keep-Alive\r\n\r\n");

return 0;

}

int HTTP_GetFileSize(int sockfd,char *path)

{

int ret = -1;

char buf_recv_tmp[10*1024+1];

Package_Url_Get_FileSize(path);

#ifdef DEBUG_HTTP

printf("send = %s \n",g_buf_send);

#endif

Send(sockfd, g_buf_send, strlen(g_buf_send), 0);

memset(buf_recv_tmp,0x0,sizeof(buf_recv_tmp));

ret = Recv(sockfd,buf_recv_tmp,sizeof(buf_recv_tmp)-1,0);

#ifdef DEBUG_HTTP

printf("recv len = %d\n", ret);

printf("recv = %s\n", buf_recv_tmp);

#endif

if(ret <= 0)

{

perror("ERR:recv fail GetFileSize()");

return -1;

}

ret = HTTP_GetContentLength(buf_recv_tmp);

if(ret <= 0)

return -1;

else

return ret;

}

/*

功能:分段下载文件

参数:

返回值:

>0----------已下载文件大小(不包含上次下载)

-1----------失败

*/

int HTTP_GetFile(int sockfd,char *path,int filelength,int download_size,char *filebuf)

{

int count;

char range[32];

int i;

int j = 0;//成功下载次数

int ret = -1;

char *p = NULL;

int download_index;//下载开始索引

count = (filelength%MAX_RECV_SIZE)?(filelength/MAX_RECV_SIZE +1):(filelength/MAX_RECV_SIZE);

download_index = download_size/MAX_RECV_SIZE;

for(i=download_index;i

{

//if(i == 20)//测试断点

//break;

if((i == (count-1))&&(filelength%MAX_RECV_SIZE))

sprintf(range,"%d-%d",i*MAX_RECV_SIZE,filelength-1);

else

sprintf(range,"%d-%d",i*MAX_RECV_SIZE,(i+1)*MAX_RECV_SIZE-1);

Package_Url_Get_File(path,range);

#ifdef DEBUG_HTTP

printf("send = %s \n",g_buf_send);

#endif

Send(sockfd, g_buf_send, strlen(g_buf_send), 0);

/*需改为提取http 返回协议头和协议体总长,然后定长接收*/

memset(g_buf_recv,0x0,sizeof(g_buf_recv));

ret = HTTP_Recv(sockfd,g_buf_recv);

if(ret < 0)

break;

if(ret == 0 )//服务端断开连接

{

sockfd = Socket_Connect(g_ip,g_port);

i--;

continue;

}

/*提取协议体数据,保存在filebuf中*/

p = strstr(g_buf_recv,"\r\n\r\n");

if(p == NULL)//jia ru duan dian baocun

{

printf("ERR:g_buf_recv not contain end flag\n");

break;

}

else

{

memcpy(filebuf+j*MAX_RECV_SIZE,p+4,MAX_RECV_SIZE);

j++;

}

}

if(i == count)

return (filelength-download_size);

else

return (i*MAX_RECV_SIZE-download_size);

}

/*

功能:HTTP下载文件

参数:

返回值:

0----------下载完成

-1---------失败

-2---------部分下载完成

注:保存文件到bin所在目录

*/

int HTTP_DownloadFile(char *url,char *save_path)

{

int ret;

int sockfd;

int filesize;

int download_size;

char filename[FILENAME_LEN+1];

char filename_bp[FILENAME_LEN+3+1];

char *filebuf;

char save_file_path[FILENAME_LEN+1];//保存下载文件的路径+文件名

char path[PATH_LEN+1];//url中的path

//提取ip和port或url(url 暂不实现,需要gethostbyname linux)

ret = HTTP_Get_IP_PORT(url,g_ip,g_port);

if(ret == -1)

return -1;

else

{

sprintf(g_host,"%s:%s",g_ip,g_port);

}

//提取下载文件名

ret = HTTP_GetFileName(url,filename);

if(ret == -1)

return -1;

ret = HTTP_GetPath(url,path);

if(ret == -1)

return -1;

//sleep(3);//debug info

//建立连接

sockfd = Socket_Connect(g_ip,g_port);

//获取下载文件总大小

filesize = HTTP_GetFileSize(sockfd,path);

if(filesize == -1)

return -1;

//#ifdef DEBUG_HTTP

printf("http need download size %d\n",filesize);

//#endif

//malloc分配存储文件空间

filebuf = (char *)malloc(filesize);

if(filebuf == NULL)

{

perror("malloc filebuf fail");

return -1;

}

else

memset(filebuf,0x0,filesize);

download_size = Get_Breakpoint(url,filename,NULL);

#ifdef DEBUG_HTTP

printf("breakpoint download_size=%d\n",download_size);//debug info

sleep(3);//debug info

#endif

//分段下载文件

ret = HTTP_GetFile(sockfd,path,filesize,download_size,filebuf);

Close(sockfd);

if(ret < 0)

{

free(filebuf);

return -1;

}

else

{

sprintf(save_file_path,"%s%s",save_path,filename);

#ifdef DEBUG_HTTP

printf("save_path=%s\n",save_path);

printf("filename=%s\n",filename);

printf("save_file_path=%s\n",save_file_path);

printf("download_size = %d\n",ret);

#endif

Save_File(filebuf,ret,save_file_path);

free(filebuf);

if((ret+download_size) == filesize)//全部下载完成

{

sprintf(filename_bp,"%s.bp",filename);

remove(filename_bp);

printf("download success\n");

return 0;

}

else//部分下载完成

{

printf("part download success\n");

//保存断点信息

Save_Breakpoint(url,save_file_path,ret+download_size,NULL);

return -2;

}

}

}

socket.c

点击(此处)折叠或打开

//socket.c

//作者:王振

#include

#include

#include

#include

#include

#include

#include

#include

#include "socket.h"

int Connect(int fd, struct sockaddr *addr, socklen_t len)

{

int result;

if ((result = connect(fd, addr, len)) == -1) {

perror("connect");

exit(EXIT_FAILURE);

}

return 0;

}

int Socket_Connect(char *ip,char *port)

{

struct sockaddr_in addr;

int sockfd;

int len;

addr.sin_family = AF_INET;

addr.sin_addr.s_addr = inet_addr(ip);//127.0.0.1为本机ip

addr.sin_port = htons(atoi(port));

len = sizeof(addr);

sockfd = socket(AF_INET, SOCK_STREAM, 0);

Connect(sockfd, (struct sockaddr*)&addr, len);

return sockfd;

}

/*

功能:向socketfd发送数据,内部实现了循环发送len长度

参数:

sockfd 是代表你与远程程序连接的套接字描述符。

msg 是一个指针,指向你想发送的信息的地址。

len 是你想发送信息的长度。

flags 发送标记。一般都设为0

返回:

0-------- 成功

退出---失败

修改:

备注:

王振

*/

int Send(int sockfd, char *sendbuf, int len, int flags)

{

int sendlen = 0;

int ret = -1;

while(sendlen < len)

{

ret = send(sockfd, sendbuf+sendlen, len-sendlen, flags);

if(-1 == ret)

{

perror("send");

exit(EXIT_FAILURE);

}

else

sendlen += ret;

}

return 0;

}

int Close(int sockfd)

{

return close(sockfd);

}

int Recv(int sockfd, char *recvbuf, int len, int flags)

{

int recv_len;

if ((recv_len = recv(sockfd, recvbuf, len, flags)) < 0)

{

perror("recv error");

exit(EXIT_FAILURE);

}

return recv_len;

}

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值