目录
基本介绍
通过 socket 编程,实现 P2P 通信
为每个 peer 开发服务器程序、客户端程序;
每个 peer 上线后,向服务器注册自己的通信信息;
假设 peer3 要下载文件 A(视频),peer1 与 peer2 都拥有 A,请设计方案使 peer3 能够同时从 peer1、peer2 同时下载该文件,例如:从 peer1 下载 A 的前 50%、同时从 peer2 下载后 50%。
设计思路
1peer 上线,将自己拥有的文件列表发送给服务器
2peer1 向服务器发起请求,询问哪里有自己想要的文件
3 服务器查询本地列表,把拥有该文件的 peer 信息(假设为 peer2 和 peer3)发送给peer1
4peer1 同时向 peer2 和 peer3 发出下载目标文件及范围的请求
5peer2 和 peer3 向 peer1 发送要求的文件
6peer1 组装文件
设计和部署过程
服务器
建立套接字
绑定套接字
监听
接收连接(客户端登录到服务器)
此时与该 peer 收发数据的套接字变为 link_id1,并且会随机分配一个端口号赋值给 clie_addr1.sin_port,这使得后面无法直接把 accept 得到的结构体发送给请求的 peer,因此我们约定好用一个端口来解决这个问题。
当然客户端单独再发一遍包含自己信息的结构体也是完全可以的,加几行代码就行了,理同udp打洞
接收peer拥有的文件列表
准备接收文件
准备接收文件内容,在实验中如果取消这一步会导致的是服务器和 peer 之间不同步,即在服务器端准备进行数据接收的时候 peer 早就完成了数据发送,使得服务器一直处于阻塞的状态
写列表
等待peer发起下载请求
在本地列表中查找该文件
找到文件,发送相应的peer信息给所求方
这里要注意,上边的代码只是把ip地址给发送给了peer,这是因为我们约定好了端口号。但是在更为普遍的情况下还是由客户端在登录的时候提供自己的服务端端口号比较好
Peer的设计部署
服务端
创建套接字
定义 peer 服务端的结构体,绑定套接字,监听
接收其他peer的连接
接收文件名并返回文件的大小
接收到属于自己的任务并发送给peer
客户端
客户端的主要任务有:连接服务器-上传文件列表-发起文件请求- 得到服务器回应-向 peer 发送请求-得到文件大小消息-预分割文件-发起下载请求-接收文件-组装文件。
连接服务器
上传文件列表
发起文件请求或者退出
接收来自服务器的peer信息
这时将得到的所有 peer 服务端信息输出,因为我们假定了已知所有 peer 服务端的端口号都一样,这是基于服务器端 accept 时改变了端口号而设置的
发送向peer的连接
发送要下载的文件名
接收文件的大小便于比较版本是否一致并分配每个peer的任务
向Peer传达自己的要求
从不同的Peer处接收文件
组装文件
至于具体函数,别问,问就是CSDN大法好
几点想说
视频通话
没有实现视频通话,我的理由是没摄像头……,但是涉及到内容包括但不限于:
摄像头麦克风的输入,音视频的编码和解码,界面的渲染,网络编程等,本质上是实时 的文件交互,个人设想是通过接收和发送两个线程实现一对一的视频通话,多人通话则 需要在每两个之间建立连接以收发数据。这个通话也可用服务器转发的方式实现,只是对服务器来说是一种巨大的负担而且浪费了网络资源。
为什么是客户端登录到服务器?
这里就有一个问题,我选择登录服务器也就是向服务器发送文件列表这一 行为由客户端来完成,因为刚开始做的时候我将这一步骤加入了服务端,但是 由于服务端不能够执行 connect,只能等待其他的连接,而由服务器来连接peer 显然是不合适的,所以我才将这一步加入到了客户端。
这一行为是没有现实意义的,因为如果 peer 可以选择只登录客户端而不登录服务端来达到只索取而不提供的目的,这显然是违背 P2P 的原则的。
后面的实验过程中,我觉得通过多线程完全可以把这一功能甚至是客户端 程序都完全纳入一个程序,这样使得 peer 不得不发送自己的文件列表,但是问题依然存在,即如果 peer 隐藏了某些文件,即提供的文件列表是缺省的, 又该如何呢?这样的行为可以通过服务器记录 peer 通过服务器下载的历史来判断 peer 是否拥有该文件。
同样的,这样的做法也有弊端,就是如果 peer 已经删除掉了文件的话发起请求的 peer 通过服务器的消息来找该 peer 的话会得到否的回答,如果有大量 peer 都经过这一错误对负责检索的服务器和下载的 peer 都是一种负担。
这一问题我认为的结局方法是由发起请求的peer 来判断。例如,如果 peer 要下载文件 A 而向服务器发起查询,得知了 B、C、D 都有该文件,A 分别向BCD 发送请求,结果 D 已经删除了该文件,这时 BC 返回有的消息和文件的大小,而 D 返回否。那么 A 原本打算分成三分来下载文件(其他条件足够), 而由于 D 的缺席,A 只能分成两份来下载。而在这之后,A 应当向服务器报告D 已经没有该文件使得客户端将 D 拥有该文件的记录删除,从而实现记录的同步
NAT穿透
穿透的本质是为了实现TCP通信,而TCP通信就要建立连接,连接的建立就需要知道对方的ip地址和端口号。那么穿透的本质目的就是为了获得ip地址和端口号。这一点在学了网络层之后自然就是非常容易理解的了,思路的话我前面有写一篇udp通信的文章,后面有解释
感慨
三天的时间从什么都不懂到写到这里,有种恍若隔世的感觉。
下面是其他两篇的链接:
计算机网络实验作业3-2-udp通信
https://blog.csdn.net/weixin_43231508/article/details/106569314
计算机网络课程作业——C/S通信
https://blog.csdn.net/weixin_43231508/article/details/106571208
算是有些收获吧,毕竟从每一个小函数写起直到整个程序。
书山有路勤为径,学海无涯苦作舟
联系邮箱:[email protected]