linux c++下载http文件并显示进度<转>

时间:2023-01-14 14:57:00
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <stdlib.h>
#include <iostream>
#include <string.h>
#include <unistd.h>
#include <fstream>
#include <vector>
#include <pthread.h>
using namespace std;
typedef string::size_type (string::*find_t)(const string& delim,
string::size_type offset) const;
vector<string> Split(const string& s,
const string& match,
bool removeEmpty=false,
bool fullMatch=false)
{
vector<string> result; // return container for tokens
string::size_type start = , // starting position for searches
skip = ; // positions to skip after a match
find_t pfind = &string::find_first_of; // search algorithm for matches
if (fullMatch)
{
skip = match.length();
pfind = &string::find;
}
while (start != string::npos)
{
string::size_type end = (s.*pfind)(match, start);
if (skip == ) end = string::npos;
string token = s.substr(start, end - start);
if (!(removeEmpty && token.empty()))
{
result.push_back(token);
}
if ((start = end) != string::npos) start += skip;
}
return result;
}
void SplitProperty(vector<string> property,string* name,string *value)
{
vector<string>::iterator it=property.begin();
if(it!= property.end())
{
name->clear();
name->append(*it);
}
it++;
if(it!= property.end())
{
value->clear();
value->append(*it);
}
}
int GetFileSize(char* host,char* file,string* error,int& headersize)
{
int size=-;
struct sockaddr_in servaddr;
struct hostent *hp;
string splitline="\r\n";
string PName;
string PValue;
string splittagbalue=":";
string info;
vector<string> properties;
vector<string> property;
int sock_id;
char message[*] = {};
int msglen;
string request;
request.append("HEAD ");
request.append(file);
request.append(" HTTP/1.1\n");
request.append("Host:");
request.append(host);
request.append("\r\n\r\n");
//Get a socket
if((sock_id = socket(AF_INET, SOCK_STREAM, )) == -) {
error->append("Couldn't get a socket!");
return size;
}
//book uses bzero which my man pages say is deprecated
//the man page said to use memset instead. :-)
memset(&servaddr,,sizeof(servaddr));
//get address for google.com
if((hp = gethostbyname(host)) == NULL) {
error->append("Couldn't access network.");
error->append(host);
return size;
}
//bcopy is deprecated also, using memcpy instead
memcpy((char *)&servaddr.sin_addr.s_addr, (char *)hp->h_addr, hp->h_length);
//fill int port number and type
servaddr.sin_port = htons();
servaddr.sin_family = AF_INET;
//make the connection
if(connect(sock_id, (struct sockaddr *)&servaddr, sizeof(servaddr)) != ) {
error->append("Couldn't connect!");
return size;
}
write(sock_id,request.c_str(),request.length());
//read the response
msglen = read(sock_id,message,*);
headersize= msglen;
info.append(message,,msglen);
close(sock_id);
properties =Split(info,splitline,true);
vector<string>::iterator it;
for (it=properties.begin(); it<properties.end(); it++)
{
property= Split(*it,splittagbalue,true);
SplitProperty(property,&PName,&PValue);
if(PName=="Content-Length")
{
size =atoi(PValue.c_str());
break;
}
}
if(size==-)
{
error->append("Resource Not Found!");
}
return size;
}
void DownloadFile(char* host,char* file,char * savefile,float size,int& progress,int hsize)
{
struct sockaddr_in servaddr;
struct hostent *hp;
string info;
int sock_id;
char message[] = {};
char messagetop[]={};
int msglen;
float readcount=;
string request;
request.append("GET ");
request.append(file);
request.append(" HTTP/1.1\n");
request.append("Host:");
request.append(host);
request.append("\r\n\r\n");
//Get a socket
if((sock_id = socket(AF_INET, SOCK_STREAM, )) == -) {
return;
}
//book uses bzero which my man pages say is deprecated
//the man page said to use memset instead. :-)
memset(&servaddr,,sizeof(servaddr));
//get address for google.com
if((hp = gethostbyname(host)) == NULL) {
return;
}
//bcopy is deprecated also, using memcpy instead
memcpy((char *)&servaddr.sin_addr.s_addr, (char *)hp->h_addr, hp->h_length);
//fill int port number and type
servaddr.sin_port = htons();
servaddr.sin_family = AF_INET;
//make the connection
if(connect(sock_id, (struct sockaddr *)&servaddr, sizeof(servaddr)) != ) {
return;
}
//NOW THE HTTP PART!!!
//send the request
write(sock_id,request.c_str(),request.length());
ofstream outfile (savefile,ofstream::binary);
do{
msglen = read(sock_id,message,);
if(msglen==)
break;
if(readcount==)
{
int tempindex=;
for(int i =hsize-;i<msglen;i++)
{
messagetop[tempindex]= message[i];
tempindex=tempindex+;
}
outfile.write (messagetop,tempindex);
}
else
{
outfile.write (message,msglen);
}
readcount=readcount+msglen;
progress=readcount/size*;
}while(readcount<=(size+ hsize));
outfile.close();
close(sock_id);
//read the response
}
float filesize;
int loadprogress=;
int hsize;
pthread_t tUpdateWork;
void* UpdateWorCoping(void* data)
{
DownloadFile("192.168.2.128","/games.data","/usr/games.data",filesize,loadprogress,hsize);
}
int main(int argc, char** argv)
{
string error;
filesize = GetFileSize("192.168.2.128","/games.data",&error,hsize);
pthread_create(&tUpdateWork,NULL,UpdateWorCoping,);
while(loadprogress<)
{
printf("Progress %d \n", loadprogress);
usleep();
}
return ;
}

在使用该代码测试的时候可能会出现的问题:

1.

在DownloadFile 内的有两处:

 char message[18000] = {0};
 char messagetop[18000]={0};
可能在运行的时候造成栈溢出,所以最好是修改为动态申请空间。
另外还有一处就是GetFileSize() 内 问题同上。

2.

在DownloadFile() 内的写文件中:

里面有个for循环:for(int i = hsize -1; i<msglen ; i++)

我在亲自测试的发现 hsize - 1 会使文件保存大小跟原始文件有出入。于是暂时改成了 for(int i = hsize ; i<msglen ; i++)

不知道对于其他类型文件是否应该 做 -1 操作。待以后再测一测看看。