微信公众号:小浩笔记
什么是断点续传?
断点续传,在我们生活中起了很大作用,也帮我们节省了很多时间,就是信号中断后(掉线或关机等),下次能够从上次的地方接着传送(一般指下载或上传),不支持断点续传就意味着下次下载或上传必须从零开始.
举个简单的例子:迅雷上次没传完,下次打开,就可以直接传了。
解决方案:
下载:多次握手,首先得到断点续传的起始位置,然后打开文件读取偏移量,从断点续传位置开始上传。写文件的时候要用追加模式;
上传:S端记录上次传的记录,下次C上传的时候,问下S上传到哪了,C就从上次传的偏移量,读文件继续上传;
而小浩做的笔记内容就是从网上下载图片,中途断开,然后再连接,是否完成下载;实现此功能,运用了TCP、HTTP等知识点。
来吧,一笔一笔来做笔记...
1.首先,要准备图片的链接,然后从链接获取主机名和文件名
/*******************************
参数
link:图片链接
host:主机名
pathname文件名
********************************/
void argParser(const char *link, char **host, char **pathname)
{
// link : "http://www.baidu.com/xxx/yyy/3453453425.jpg";
char *h, *p;
h = p = link;
char *delim1 = "http://";
char *delim2 = "https://";
if(strstr(link, delim1) != NULL)
{
h += strlen(delim1);
}
else if(strstr(link, delim2) != NULL)
{
h += strlen(delim2);
}
p = strstr(h, "/");
if(p == NULL)
{
printf("非法连接!\n");
exit(0);
}
*host = calloc(1, 256);
*pathname = calloc(1, 1024);
memcpy(*host, h, p-h);
memcpy(*pathname, p, strlen(p));
}
2.获取服务器IP,然后使用套接字连接服务器
// 获取服务器I
struct hostent * he = gethostbyname(host);
struct in_addr serverIP = *(struct in_addr*)he->h_addr_list[0];
int sockfd = Socket(AF_INET, SOCK_STREAM, 0);
struct sockaddr_in addr;
socklen_t len = sizeof(addr);
addr.sin_family = AF_INET;
addr.sin_addr = serverIP;
addr.sin_port = htons(80);
Connect(sockfd, (struct sockaddr *)&addr, len);
printf("连接成功!\n");
3.(断点续传来了....)准备好本地文件,判断文件释放已经下载过,然后将文件的缓冲类型设置为不缓冲,提前谈一谈,主要是begin这个变量,如果文件已经下载过,获取该文件的大小,使写的光标偏移的文本,然后也使读端(即读图片),使光标偏移到断开下载的地方。
// 准备好本地文件
char *filename = strrchr(pathname, '/')+1;
// 判断文件释放已经下载过
FILE *fp = NULL;
long begin = 0;
if(access(filename, F_OK))
fp = fopen(filename, "w");
else
{
struct stat fileinfo;
bzero(&fileinfo, sizeof(fileinfo));
stat(filename, &fileinfo);
begin = fileinfo.st_size;
fp = fopen(filename, "a");
}
// 将文件的缓冲类型设置为不缓冲
setvbuf(fp, NULL, _IONBF, 0);
4.发送HTTP请求(begin这个变量也发过去了),然后收到响应头部,然后分析响应头部,获取内容大小
// 发送HTTP请求
char *httpRequest = calloc(1, 2048);
snprintf(httpRequest, 2048, "GET %s HTTP/1.1\r\n"
"Host: %s\r\n"
"Range: bytes=%d-\r\n"
"\r\n", pathname, host, begin);
write(sockfd, httpRequest, strlen(httpRequest));
// 接收响应头部
char *httpResponse = calloc(1, 2048);
int total = 0;
while(1)
{
total += read(sockfd, httpResponse+total, 1);
if(strstr(httpResponse, "\r\n\r\n"))
break;
}
printf("响应头部:\n%s", httpResponse);
long length = getLen(httpResponse);
printf("即将要下载的大小:%d\n", length);
5.然后接受响应正文,把图片从断点下载下来,完璧归赵。
// 接收响应正文
char *content;
content = calloc(1, 10*1024);
total = 0;
total += begin;
while(length > 0)
{
bzero(content, 10*1024);
int n = read(sockfd, content, 10*1024);
fwrite(content, n, 1, fp);
length -= n;
total += n;
printf("已经下载: %d【字节】\r", total);
}
这个断点续传下载图片的基本框架就是这样子了。