我正试图使BitTorrent客户端仅用于学习目的。我将首先解释到现在为止我做了什么。
我创建了一个线程池,其大小是可用处理器的数量。现在,每个种子文件都在单独的线程上执行。我从每个种子文件中获取跟踪器URL列表,并使用java NIO发送连接请求。我假设我几乎同时发送所有连接请求,因为我只是循环访问URL列表并发送请求。因此,我不再维护每个URL的超时,而是在发送第一个请求时启动计时器。当计时器超时时,我将连接请求重新发送到所有未响应的URL。 (在这里,对于计时器,我计算interval
并跟踪开始时间startTime
。我循环播放,直到currentTimeMillis()
超过startTime+interval
。在循环中,我使用selector.select()
选择准备就绪的channels
。何时循环退出,我将连接请求重新发送到所有未响应的通道。我未使用Timer
和ScheduledExecutorService
,因为我认为这可能导致线程数量爆炸。我已经在每个文件中使用一个线程。我对此提出建议,并提出更好的方法)。
主要问题来自发送公告请求。我可能会在任何时间收到连接响应,这时我应该发送宣告请求并启动计时器,如果请求超时则重新发送该请求。当前,对于每个连接响应,我向线程池提交一个Runnable
任务,该任务处理宣告请求的发送。所以我在每个宣布请求-响应周期中使用一个线程。因此,如果我有许多种子文件,并且每个文件至少有5个跟踪器URL,这不会造成性能问题吗?有什么更好的方法可以使此功能生效?如果有人能够对torrent客户端如何同时处理如此多的文件和如此多的请求响应周期有所了解,那将是非常有启发性的。
最佳答案
使用nio或基于选择器的库(如netty)的主要好处是允许您将多个网络连接多路复用到单个对象中,而仅等待该对象。多重定义为comprising several interleaved parts。在这种情况下,您将以前需要单个线程等待的多个网络连接编织到一个仅需要单个线程等待的单个概念对象(Selector)中。在您的情况下,您可以将整个网络连接编织到一个选择器中,然后在单个线程中等待。这样就不会增加线程数量。以这种方式重新架构系统可能需要为每个连接做更多的簿记工作,这些工作以前在负责处理该连接的线程中是本地的。
另外,请勿使用System#currentTimeMillis来计算时差,而应使用System#nanoTime,因为它不会受到系统时间的任何更改的影响。