项目[P2P文件下载器]

项目介绍:

该项目完一个在局域网中进行附近文件共享下载功能的工具。

  1. 能够进行搜索匹配局域网中运行工具的主机,获取到局域网在线的主机列表
  2. 能够获取在线主机所共享的文件信息列表
  3. 能够对指定主机上的文件进行多进程分块下载来提高传输效率

 

P2P下载器功能流程:

  1. 局域网中的主机发现 找到局域网中有哪儿些主机在使用P2P下载器
  2. 看一下附近的主机,有哪儿些文件时共享的
  3. 将这个主机上的共享文件下载下来

客户端与服务端进行数据通信:

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.向指定的主机发送文件数据获取请求

 

实现流程:

  1. 发现局域网附近的共享用户
    
       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         } 
      

     

  2. 列出附近用户列表,并选择想要查看的用户主机

                  //打印配对成功的主机列表
       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         }
    

     

  3. 获取指定用户的文件列表,并选择想要下载的文件
    
       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         }
    

     

  4. 获取文件的头信息(主要是获取文件的长度信息)
    
       84 void GetFileData(const httplib::Request &req, httplib::Response &rsp){
       85     //  /list/abc.txt -> root/list/abc.txt
       86     
       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 }
    

     

  5. 对获取到的文件长度进行分区域划分下载
    
       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  -> filename
      113                 // ./download/ + filename -> ./download/filename
      114                 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         }
    
    

     

  6. 创建多进程进行分块文件下载

 

主要功能的接口设计

服务器功能接口:

  1. 提供客户端的主机配对功能
    void GetHostPair(const httplib::Request &req, httplib::Response &rsp)

     

  2. 提供客户端的文件列表获取功能

    void GetFileList(const httplib::Request &req, httplib::Response &rsp)

     

  3. 提供客户端的文件下载功能
    void GetFileData(const httplib::Request &req, httplib::Response &rsp)

     

客户端功能接口:

  1. 提供能够发现匹配局域网附近主机的功能
     bool GetHostList(std::vector<std::string> &list)

     

  2. 提供能够获取指定主机共享文件列表的功能
     bool GetFileList(std::string &host_addr)

     

  3. 提供能够下载指定主机下指定共享文件的功能
    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_BROADCAST
       bit is set in `ifa_flags', then `ifa_broadaddr' is valid. If the
       IFF_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
    # endif
    void *ifa_data;       /* Address-specific data (may be unused). */
};

但要注意的是如果是在LINUX系统下进行的操作,那么就必须要关闭防火墙

su root
systemctl stop firewalld

 

 

 

 

 

 

 

 

 

 

 

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值