#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 = 0, // starting position for searches skip = 1; // 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 == 0) 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=-1; 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[1024*1024] = {0}; 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, 0)) == -1) { 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,0,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(80); servaddr.sin_family = AF_INET; //make the connection if(connect(sock_id, (struct sockaddr *)&servaddr, sizeof(servaddr)) != 0) { error->append("Couldn't connect!"); return size; } write(sock_id,request.c_str(),request.length()); //read the response msglen = read(sock_id,message,1024*1024); headersize= msglen; info.append(message,0,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==-1) { 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[18000] = {0}; char messagetop[18000]={0}; int msglen; float readcount=0; 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, 0)) == -1) { return; } //book uses bzero which my man pages say is deprecated //the man page said to use memset instead. :-) memset(&servaddr,0,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(80); servaddr.sin_family = AF_INET; //make the connection if(connect(sock_id, (struct sockaddr *)&servaddr, sizeof(servaddr)) != 0) { 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,18000); if(msglen==0) break; if(readcount==0) { int tempindex=0; for(int i =hsize-1;i<msglen;i++) { messagetop[tempindex]= message[i]; tempindex=tempindex+1; } outfile.write (messagetop,tempindex); } else { outfile.write (message,msglen); } readcount=readcount+msglen; progress=readcount/size*100; }while(readcount<=(size+ hsize)); outfile.close(); close(sock_id); //read the response } float filesize; int loadprogress=0; 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,0); while(loadprogress<99) { printf("Progress %d \n", loadprogress); usleep(1000000); } return 0; }
在使用该代码测试的时候可能会出现的问题:
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 操作。待以后再测一测看看。