P2P下载器

  • 一、P2P下载器功能简介
  • 二、客户端功能细分
    • 1、获取在线主机
      • 1.1 获取网卡信息,得到局域网中的所有IP地址列表
      • 1.2 逐个对IP地址列表的主机发送配对请求
      • 1.3 配对得到响应,将在线主机列表的IP地址加入到on_line_host列表中
    • 2、获取指定主机文件列表
    • 3、下载指定主机上的指定共享文件
  • 三、服务端功能细分
    • 3.1 接收到主机配对请求,则做出200响应
    • 3.2 接收到文件列表请求
    • 3.3 接收到指定文件下载请求
  • 四、项目总结

一、P2P下载器功能简介

P2P下载器是一个可以跨Windows和Linux平台的点对点下载器项目,客户端通过搜索出局域网中的在线主机,向指定主机发送建立连接请求,获取主机共享文件列表,发送文件下载请求,服务端对客户端的请求做出响应,最终实现不同主机间共享文件的目的。

客户端

1.查看网络中的共享主机,向网络中广播配对请求,得到相应在线主机列表
2.选择一个主机获取文件列表,向指定主机发送获取列表请求
3.基于线程池,将文件的所有数据下载进行分块传输。
客户端的主要接口:

Client
{//获取在线主机,向所有IP地址发送配对请求,判断是否在线
public:void HostPiar(uint32_t ip)  //直接向主机发送配对请求,判断是否在线bool GetOnlineHost() //获取网卡信息,得到每个主机的IP地址bool GetShareList(uint32_t)//获取指定主机的共享文件列表,打印出来供用户选择bool Download(uint32_t ip,const std::string &filename);
private:std::vector<uint32_t> _online_host_list; //保存所有在线主机IP信息
}

服务端
1、网络通信模块:基于HTTP协议实现http服务器的搭建,实现客户端主机匹配、列表获取、文件下载的功能
2、文件操作模块:基于boost库中文件操作,实现指定目录下的文件信息迭代获取,以及文件的基本读写操作
服务端的主要接口:

//服务端模块
Server
{//1、搭建http服务器,对http请求进行业务处理作出相应 --使用第三方库httplib实现//2、针对客户端的功能请求进行业务处理接口的实现即可void HostPair()//针对客户端主机配对请求做出正确响应void ShareList()//针对客户端的获取共享文件列表做出正确响应void Download() //针对客户端下载请求做出正确响应
}

二、客户端功能细分

1、获取在线主机

1.1 获取网卡信息,得到局域网中的所有IP地址列表

  • 由于UDP协议自带局域网广播功能,但是可能丢包,我们采用tcp协议。
  • 获取所有主机IP地址。(IP地址组成:网络号+最大主机号)
//网络号=本机IP地址 和 子网掩码相与   最大主机号:子网掩码取反
//以下是获取本机网卡信息
Adapter
{public:uint32_t _ip_addr;  //网络字节序的IP地址uint32_t _mask_addr; //子网掩码信息
}
AdapterUtil
{#ifdef _WIN32static GetALLAdapter(std::vector<Adapter> *list) //获取所有网卡信息,返回信息数组//由于在求得的 0~最大主机号 中,全0和全1是不能用的,实际上我们取出的是 1-254
#elsestatic GetALLAdapter(std::vector<Adapter> *list) //获取所有网卡信息,返回信息数组
}

当然我们是先获取到所有在线主机,存入vector数组,才逐个的发送配对请求

1.2 逐个对IP地址列表的主机发送配对请求

想要对所有在线主机发送配对请求,就必须采用多线程。每对一个主机发送配对请求,就分配一个线程
在这里,主机配对的线程入口函数是void HostPair(this,uint32_t ip),将这个函数作为参数传进主机配对的每个线程函数,即就是:

这里最重要的就是主机配对函数—HostPair(this,uint32_t)应该怎么写?需要用到第三方库httplib,搭建服务器

httplib的处理流程对于客户端来说:
1、它想要配对,必然是自己作为一个客户端给对方发送请求,那这里就要先组织一个http请求数据
2、搭建tcp客户端,与主机建立连接,发送请求数据
3、等待服务器的响应,接收响应数据
4、对响应数据进行解析,判断状态码,200则表示配对成功对于服务端来说:
1、搭建一个最简单的tcp服务器
2、等待客户端发送的数据
3、按照http的协议格式,对数据进行解析
4、根据请求的资源路径以及字符串进行业务处理
5、组织http协议格式的响应,返回给客户端

关于客户端和服务端的交互,我们就必须要说httplib中最重要的两个类:

那我们接下来就是存放主机的配对结果,成功则为true,失败则为false

1.3 配对得到响应,将在线主机列表的IP地址加入到on_line_host列表中

2、获取指定主机文件列表

接口设计: bool GetShareList(std::string &host_ip)
主要流程:

向服务器发送一个文件列表获取请求
1、先发送请求
2、得到相应之后,解析正文

3、下载指定主机上的指定共享文件

接口:DownloadFile()
本质:网络通信中的文件传输,其实传输的是文件数据,对方打开文件,读取文件数据发送,接收方,创建文件,接收到文件数据,写入文件中,主要流程如下:

1、向服务端发送文件下载请求
2、得到相应结果,相应结果中的body正文就是文件数据
3、创建文件,将文件数据写入新建的文件中,关闭文件

这里就涉及到我们的一个文件操作,所以我们定义了专门的文件操作类,主要包含以下操作:

三、服务端功能细分

服务端主要是对客户端的请求作出响应,比如我们之前在httplib库搭建服务器的时候,客户端会使用Get请求方法,组织成一个数据发送给服务端,这里服务端的响应是:

//添加针对客户端请求处理方式对应关系:_srv.Get("/hostpair", HostPair);  //如果我们接收到hostpair这个请求,那就调用HostPair这个请求方法,后面的函数也是类似功能,很对不同的请求,找到对应的外部函数请求方法_srv.Get("/list", ShareList);_srv.Get("/download/.*",Download); //正则表达

3.1 接收到主机配对请求,则做出200响应

3.2 接收到文件列表请求

1、检测获取指定共享目录下的文件信息
2、将所有文件名组织成为http响应正文

3.3 接收到指定文件下载请求

接口:static void Download(const httplib::Request &req, httplib::Response &rsp)
流程如下:
1、通过文件名检测文件是否存在,若不存在则需要重新创建

boost::filesystem::exists(SHARED_PATH)
boost::filesystem::create_directory(SHARED_PATH);

2、检测文件是否是一个普通文件,是的话就浏览这个目录下的文件信息(选择使用C++boost库中提供的文件系统接口实现目录的浏览,以及文件的各种操作)
3、打开文件读取文件数据作为http相应正文给客户端
那这里涉及到一个文件的分块下载问题:
HTTP协议头中的Range字段,告诉服务器一个文档区间范围,若文件大小超过100M,则采用分块传输。所以我们提前得知道文件有多大,HEAD清秀方法就可以解决:HEAD/filename HTTP 1.1只要响应头信息而不要内容,通过响应正文的Content-Length可以知道正文有多大。以下是分块下载的主要代码分析:

bool RangeDownLoad(const std::string &host_ip,const std::string &name)
{//1、发送HEAD请求,通过响应中的Content——Length获取文件大小auto rsp=cli.Head(req_path,c_str);std::string clen = rsp->get_header_value("Content-Length");int64_t filesize = StringUtil::Str2Dig(clen);//2、根据文件大小进行分块// 若文件大小能整除块,则分块个数等于文件大小除以分块大小range_count = filesize / MAX_RANGE;  //计算块数for (int i = 0; i < range_count; i++){range_start = i * MAX_RANGE;  //起始分块if (i == (range_count - 1))  //末尾分块{range_end = filesize - 1;}else{//除不尽MAX_RANGE就需要+1range_end = ((i + 1)*MAX_RANGE) - 1; }}//3、注意请求分块数据,得到响应之后写入文件的指定位置tmp << "bytes=" << range_start << "-" << range_end;   //组织一个range头信息的区间值httplib::Headers header;header.insert(std::make_pair("Range", tmp.str())); header.insert(httplib::make_range_header({ {range_start,range_end} }));auto rsp=cli.Get(req_path.c_str(), header);if (rsp == NULL || rsp->status != 206){std::cout << "区间下载失败\n";return false;}std::cout << "客户端分块写入文件"<<range_start<<"-"<<range_end<<std::endl;FileUtil::Write(name, rsp->body, range_start);
}  //将下载文件写入文件指定位置

四、项目总结

4.1 项目采用多线程的地方
1、在进行获取完所有的在线主机,我们得向这些主机发送配对请求,这里就采用多线程,发送一个配对请求,启动一个线程,将线程入口函数传递进去。
2、httplib库里面的多线程
3、分块传输的时候采用多线程,因为数据在比较大的情况下,需要分块,传输每个模块的时候,启动一个线程
4.2 项目中存在的问题
(1)httplib版本的适配,网卡信息的获取,分块传输
(2)在分块传输中,由于文件太大,httplib是将文件数据直接放到内存中进行传输,占用大量内存,传输大文件的时候,若内存不够用,存在两种情况:
1)因此产生了大量的内存交换,极大地降低了效率
2)内存不够程序直接崩溃
一开始分多少块,就启动多少线程,并没有解决实际问题,后来采用线程池,限制了同一时间能发送的请求数以及避免同一时间占用内存过多的情况。
(3)在文件下载过程中,下载文件的内容完全一致但是大小不一致,我们利用dos窗口,进入源文件和下载文件,实用工具MD5比较两个文件的大小是否一致(MD5对文件的所有内容进行运算,最终得到一个32字节的字符串,然后可以比较两个字符串是否相同),发现问题出在标准文件操作的问题,使用C++标准库进行文件操作的时候,默认是文本操作,会涉及到字符编码集的宽字符问题,导致读取的数据与写入数据不同,因此在读写文件的时候采用二进制方式读写(wb)问题成功解决

4.3 项目涉及的其他技术
断点续传:当正在下载文件的时候,网络突然中断,导致文件没有下载完毕而失败,等到下次网络通畅的时候,可以继续从上次断开的位置开始下载文件,而不需要重新下载,来提高异常情况下文件的传输效率

HTTP协议中的数据传输就支持分块传输,但是咱们暂时不实现断点续传,我们使用的是分块传输。
4.4 项目的测试点
功能测试:是否能够正常完成下载功能
性能测试:服务端通信能够同时与多少个客户端进行数据通信,文件传输性能
使用了webbench测试工具对连接数进行了测评,因为虚拟机以及云服务器的资源限制,当前可以连接几十个,再多就会链接失败
对于文件传输,在程序中打了log,对文件传输其实以及结束时间进行时间统计,一个客户端传输50M/s;
4.5 项目的可扩展点
1)网络穿透技术:当前我们所写的p2p程序是一个局域网的p2p无法实现整个网络的p2p,因为局域网之间无法跨网络进行数据传输(因为局域网中的主机都是用的私网地址,并且通过NAPT映射之后才能通信),就算是我们知道对方的私网地址,但是因为我们并没有网络因此无法直接通信,局域网之间想要进行通信就要借助网络穿透技术实现
2)epoll多路转接技术实现总线时间监控
3)视频的直接点播

【Linux项目】 --P2P下载器的详细介绍相关推荐

  1. P2P下载器(Linux下C++项目实战)

    P2P下载器:即点对点下载器,服务端与客户端.服务端共享文件列表,客户端配对相应服务端,下载所需要的文件. 一.项目介绍 1.项目功能 搜索附近(局域网内)在线用户, 此处不足(只能在局域网内获取,需 ...

  2. 【实战项目】---P2P下载器

    P2P下载器 1.引言 2.项目简介 3.整体框架 4.服务端设计 5.客户端设计 6.主要功能端口 7.httplib的处理流程: 8.源码 1.引言 在校期间经常需要进行给学委,班长拷贝文件.互传 ...

  3. 项目[P2P文件下载器]

    项目介绍: 该项目完一个在局域网中进行附近文件共享下载功能的工具. 能够进行搜索匹配局域网中运行工具的主机,获取到局域网在线的主机列表 能够获取在线主机所共享的文件信息列表 能够对指定主机上的文件进行 ...

  4. Linux学习笔记 文件服务Vsftp详细介绍

    Linux学习笔记 文件服务Vsftp详细介绍 知识点: 1.FTP使用TCP连接和TCP端口 2.在进行通信时,FTP需要建立两个TCP连接: 一个用于控制信息,TCP端口号缺省为21 一个用于数据 ...

  5. Linux文件系统中的inode节点详细介绍

    一.inode是什么? 理解inode,要从文件储存说起. 文件储存在硬盘上,硬盘的最小存储单位叫做"扇区"(Sector).每个扇区储存512字节(相当于0.5KB). 操作系统 ...

  6. Linux下curses函数库的详细介绍

    Linux下curses函数库的详细介绍 curses库介绍 安装 curses库函数介绍 初始化和重置函数 管理屏幕的函数 输出到屏幕 从屏幕读取 清除屏幕 移动光标 字符属性 管理键盘的函数 键盘 ...

  7. linux版本的redis bin,redis-4.0.2.tar.gz for centos的linux系统版本下载(安装详细步骤)...

    一.前言 本章通过redis-4.0.2.tar.gz在Centos的linux操作系统上进行安装(redis的详细介绍操作配置命令&window版本参见其他文章),下方在SSH命令终端进行详 ...

  8. linux 根目录下各个目录的详细介绍

    当我们在我们的家目录里不停的向上推,"cd .."操作时我们返现,也就推到这个地方到头了.而被我们用户使用的也就是home 目录,那么其他目录都是干什么的呢?让我们一一揭秘. bi ...

  9. Linux内核架构:CPU架构详细介绍(图例解析)

    一. 概述 CPU架构是CPU商给CPU产品定的一个规范,主要目的是为了区分不同类型的CPU.目前市场上的CPU分类主要分有两大阵营,一个是intel.AMD为首的复杂指令集CPU,另一个是以IBM. ...

最新文章

  1. 微寻,把“线下医院”带到“线上轻松问诊”
  2. vue 打印 canvas 显示空白
  3. Oracle 用户概念与基本操作
  4. thinkPHP开发基础知识 包括变量神马的
  5. IDEA 15款神级良心插件强烈推荐收藏
  6. 炫酷实用的jQuery插件 涵盖菜单、按钮、图片
  7. html仿qq最小化怎么实现,JS仿QQ好友列表展开、收缩功能(第一篇)
  8. python三角函数拟合_使用python进行数据拟合最小化函数
  9. MacOS emacs Command “pyls“ is not present on the path.报错及解决
  10. 随笔 - Andriod
  11. 周立功开发板安装linux,极速搭建周立功IMX283A ARM Linux开发环境(1)
  12. 软件测试中的测试报告
  13. RL之MAB:多臂老虎机Multi-Arm Bandit的简介、应用、经典案例之详细攻略
  14. MySQL的数据类型(三)
  15. 数字IC后端实现40天速成篇(中)
  16. jQuery实现图片点击放大缩小(小案例)
  17. 高中数学数列技巧解题秒杀视频:数列小题秒杀技巧
  18. 听说C语言很难?怎么不来看看我这篇(六)结构
  19. 【Python】浅谈 字节码 + 虚拟机 (Python 解释器)
  20. 【STM32F407的DSP教程】第22章 DSP矩阵运算-放缩,乘法和转置矩阵

热门文章

  1. php5.6 dbase,dBase数据库
  2. 等保测评--网络安全等级保护测评过程指南
  3. 超详细面经分享!@最近考虑换工作的伙伴们
  4. python窗口制作_python制作一个桌面便签软件
  5. autware.auto foxy
  6. 可怕的QQ浏览器,仅仅开了一个网页,就用了我980M,怎么回事儿?firefox性能依旧坚挺
  7. 软件测试周刊(第49期):什么是大众?大众就是那些一年读书不超过五本的人。
  8. 移动通信增值业务概述
  9. 机器视觉 | 光源照明综述(详细版)
  10. 软件开发就像歌曲制作,我的岗位相当于乐器伴奏