生活随笔
收集整理的這篇文章主要介紹了
项目[P2P文件下载器]
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
項目介紹:
該項目完一個在局域網中進行附近文件共享下載功能的工具。
能夠進行搜索匹配局域網中運行工具的主機,獲取到局域網在線的主機列表 能夠獲取在線主機所共享的文件信息列表 能夠對指定主機上的文件進行多進程分塊下載來提高傳輸效率
?
P2P下載器功能流程:
局域網中的主機發現 找到局域網中有哪兒些主機在使用P2P下載器 看一下附近的主機,有哪兒些文件時共享的 將這個主機上的共享文件下載下來
客戶端與服務端進行數據通信:
1.通信協議的選擇:HTTP超文本傳輸協議
HTTP協議格式
? ? ? ? ? ?首行:
? ? ? ? ? ? ? ? ? ? ?請求首行:請求方法 GET? url 協議版本 1.1 \r\n
? ? ? ? ? ? ? ? ? ? ? 響應首行:協議版本 響應狀態碼 狀態碼描述信息\r\n
? ? ? ? ? ? ?頭部:以 key-value 的形式組成一個鍵值對,并且鍵值對之間以\r\n進行間隔
? ? ? ? ? ? ? 空行:
? ? ? ? ? ? ? 正文:
?
處理細節:
1.一個主機如何發現附近的主機--下載器之間的通信
一個主機將一個主機的配對需求,發送到局域網中的所有主機上,這時候如果有主機運行了P2P下載器程序,則對這個請求,進行一個配對回應。
2.查看附近主機的共享文件
向指定的主機發送一個獲取文件列表的請求
附近主機收到請求后,則將共享的目錄下的所有文件名相應出去
3.下載指定文件
向指定的主機發送獲取文件的請求
指定主機收到請求后,則打開指定文件
?
?
?服務器端設計:
設計實現HTTP服務端程序,能夠提供瀏覽器客戶端進行文件的下載,獲取文件列表功能
服務端流程:
1.搭建HTTP服務器
? ? ? ?1.主機配對請求處理功能
? ? ? ?2.主機文件列表獲取處理功能
? ? ? ?3.主機獲取數據獲取功能
2.能夠提供附近主機配對功能
3.能夠像附近主機提供文件列表
4.能夠向附近主機提供文件下載功能
?
客戶端設計
實現基于服務器HTTP的分塊傳輸功能實現多進程文件分塊下載功能的下載器,通過分塊傳輸提高傳輸效率
客戶端流程:
1.獲取局域網中所有的IP地址信息
2.向獲取到的IP主機地址發送主機配對請求--獲取到配對成功的主機IP地址列表
3.打印配對成功的主機列表
4.用戶選擇想要獲取哪兒個主機的共享文件
5.向指定的這個主機發送文件列表獲取請求 -- 獲取到主機上的共享文件列表
6.打印所有的文件列表,用戶選擇想要下載哪兒個共享文件
7.向指定的主機發送文件數據獲取請求
?
實現流程:
發現局域網附近的共享用戶 23 //1. 獲取局域網IP地址列表24 bool GetHostList(std::vector<std::string> &list) {25 struct ifaddrs *addrs;26 getifaddrs(&addrs);27 while (addrs) {28 if (strcmp(addrs->ifa_name, "lo") == 0) {29 addrs = addrs->ifa_next;30 continue;31 }32 sockaddr_in *addr=(sockaddr_in*)addrs->ifa_addr;33 sockaddr_in *mask=(sockaddr_in*)addrs->ifa_netmask;34 if (addr->sin_family != AF_INET) {35 addrs = addrs->ifa_next;36 continue;37 }38 uint32_t net = ntohl((addr->sin_addr.s_addr & mask->sin_addr.s_addr)) ;39 int host = ntohl(~mask->sin_addr.s_addr);40 41 for (int i = 1; i < host; i++) {42 struct in_addr ip;43 ip.s_addr = htonl(net + i);44 list.push_back(inet_ntoa(ip));45 }46 addrs = addrs->ifa_next;47 } 48 return true;49 } ?
列出附近用戶列表,并選擇想要查看的用戶主機
//打印配對成功的主機列表67 void PrintHost() {68 for (int i = 0; i < _host_list.size(); i++) {69 std::cout <<i<<". [" << _host_list[i] << "]\n";70 }71 SelectHost();72 }73 //用戶選擇要獲取哪個主機的文件列表74 void SelectHost() {75 std::cout <<"選擇想要查看的主機:";76 fflush(stdout);77 std::cin >> _host_idx;78 GetFileList(_host_list[_host_idx]);79 }
?
獲取指定用戶的文件列表,并選擇想要下載的文件 80 //3.獲取指定主機的文件列表81 bool GetFileList(std::string &host_addr) {82 httplib::Client client(host_addr.c_str(), 9000);83 auto rsp = client.Get("/list");84 if (rsp && rsp->status == 200) {85 //filename1\nfilename2...86 boost::split(_file_list, rsp->body, boost::is_any_of("\n"));87 }else {88 std::cerr<<"host:["<<host_addr<<"] get file list failed\n";89 }90 return true;91 }92 //4.打印文件列表93 void PrintFile() {94 for (int i = 0; i < _file_list.size(); i++) {95 std::cout << i << ". ["<<_file_list[i]<<"]\n";96 }97 SelectFile();98 }
?
獲取文件的頭信息(主要是獲取文件的長度信息) 84 void GetFileData(const httplib::Request &req, httplib::Response &rsp){85 // /list/abc.txt -> root/list/abc.txt86 87 std::cerr << "download file:["<<req.path<<"]\n";88 std::string realpath = ROOT_PATH + req.path;89 if (!bf::exists(realpath)) {90 std::cerr << "file:["<<realpath<<"] is not exists\n";91 rsp.status = 404;92 return;93 }94 //bf::file_size() 獲取文件大小95 int64_t fsize = bf::file_size(realpath);96 97 std::ifstream file(realpath, std::ios::binary);98 if (!file.is_open()) {99 rsp.status = 500;100 return ;101 }102 rsp.body.resize(fsize);103 file.read(&rsp.body[0], fsize);104 if (!file.good()) { 105 rsp.status = 500;106 return;107 }108 file.close();109 110 rsp.set_header("Content-Type", "application/octet-stream");111 rsp.status = 200;112 return ;113 }
?
對獲取到的文件長度進行分區域劃分下載 99 //選擇文件進行下載100 bool SelectFile() {101 std::cout<<"選擇想要下載文件id:";102 fflush(stdout); 103 std::cin >> _file_idx;104 DownLoadFile(_file_list[_file_idx]);105 return true;106 }107 bool DownLoadFile(std::string &filename) {108 std::string host_addr = _host_list[_host_idx];109 httplib::Client client(host_addr.c_str(), 9000);110 auto rsp = client.Get(filename.c_str());111 if (rsp && rsp->status == 200) {112 // /list/filename -> filename113 // ./download/ + filename -> ./download/filename114 boost::filesystem::path path(filename);115 std::string file = path.filename().string();116 std::string realpath = _download_path + file;117 std::ofstream fs(realpath, std::ios::binary);118 if (!fs.is_open()) {119 std::cerr << "open file:["<<realpath<<"] failed\n";120 return false;121 }122 fs.write(&rsp->body[0], rsp->body.size());123 if (!fs.good()) {124 std::cerr << "write file:["<<realpath<<"] failed\n";125 return false;126 }127 fs.close();128 std::cerr << "download file success\n";129 }else {130 std::cerr << "download file failed!\n";131 return false;132 }133 return true;134 } ?
創建多進程進行分塊文件下載
?
主要功能的接口設計
服務器功能接口:
提供客戶端的主機配對功能 void GetHostPair(const httplib::Request &req, httplib::Response &rsp) ?
提供客戶端的文件列表獲取功能
void GetFileList(const httplib::Request &req, httplib::Response &rsp) ?
提供客戶端的文件下載功能 void GetFileData(const httplib::Request &req, httplib::Response &rsp) ?
客戶端功能接口:
提供能夠發現匹配局域網附近主機的功能 bool GetHostList(std::vector<std::string> &list) ?
提供能夠獲取指定主機共享文件列表的功能 bool GetFileList(std::string &host_addr) ?
提供能夠下載指定主機下指定共享文件的功能 bool DownLoadFile(std::string &filename)
其它接口包括httplib.h的基本使用?
及ifaddr.c:
struct ifaddrs *addrs = NULL;
int getifaddrs(struct ifaddrs **ifap); /*獲取本機網卡信息*/返回值:0-成功 -1-失敗
void freeifaddrs(struct ifaddrs *ifa); /*釋放網卡信息存儲資源*/
struct ifaddrs
{struct ifaddrs *ifa_next; /* 鏈表指針,指向下一個網卡信息 */char *ifa_name; ? ? ? /* 網卡名稱*/unsigned int ifa_flags; ? /* Flags as from SIOCGIFFLAGS ioctl. */struct sockaddr *ifa_addr; ? /* 地址結構*/struct sockaddr *ifa_netmask; /* 子網掩碼*/union{/* At most one of the following two is valid. If the IFF_BROADCASTbit is set in `ifa_flags', then `ifa_broadaddr' is valid. If theIFF_POINTOPOINT bit is set, then `ifa_dstaddr' is valid.It is never the case that both these bits are set at once. */struct sockaddr *ifu_broadaddr; /* Broadcast address of this interface. */struct sockaddr *ifu_dstaddr;/* Point-to-point destination address. */} ifa_ifu;# ifndef ifa_broadaddr# define ifa_broadaddr ifa_ifu.ifu_broadaddr# endif# ifndef ifa_dstaddr# define ifa_dstaddr ? ifa_ifu.ifu_dstaddr# endifvoid *ifa_data; ? ? ? /* Address-specific data (may be unused). */
};
但要注意的是如果是在LINUX系統下進行的操作,那么就必須要關閉防火墻
su root
systemctl stop firewalld
?
?
?
?
?
?
?
?
?
?
?
總結
以上是生活随笔 為你收集整理的项目[P2P文件下载器] 的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔 網站內容還不錯,歡迎將生活随笔 推薦給好友。