Azureus源码剖析(三)

本文涉及的产品
云解析 DNS,旗舰版 1个月
全局流量管理 GTM,标准版 1个月
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
简介:
 接着第一篇的工作,本篇继续分析种子文件监听服务器的实现细节。

先简单描述下其工作流程,首先服务器在6880端口处开启一个套接字监听,然后开启一个守护线程用于处理到来的“打开种子文件列表”请求,在这个服务线程中不断循环读取来自客户的请求,对torrent文件列表进行解析。如果此时Azureus的各个组件都已经创建完毕,则说明Azureus的核心处理组件可用,则直接对torrent文件列表进行处理,否则,先将torrent文件列表加入种子文件队列中,等到各个组件创建完毕后再来处理种子队列中的各个种子文件。

下面来查看其源代码,先看其成员变量:

复制代码
private ServerSocket socket;//服务器套接字
    private int state;//服务器当前状态
    private boolean bContinue;//服务线程是否继续运行
    public static final int STATE_FAULTY = 0;//错误状态
    public static final int STATE_LISTENING = 1;//监听状态
    protected List queued_torrents = new ArrayList();//待解析种子文件队列,这里并没有考虑同步互斥的问题
protected boolean core_started    = false;//核心处理组件是否已经启动
复制代码
在其构造函数中完成服务器的创建和启动:
            socket = new ServerSocket(6880, 50, InetAddress.getByName("127.0.0.1")); //NOLAR: only bind to localhost
        
            state = STATE_LISTENING;//设置服务器状态为“监听”
而在pollForConnections中为Azureus添加了一个生命周期监听器,这样当其所有组件完成时就会通知此监听器,而后者会调用openQueuedTorrents方法,对待解析种子文件队列中排队的种子文件进行处理,此外,这个方法中还创建了一个守护线程用于处理到来的打开种子文件列表请求,实际的处理工作在pollForConnectionsSupport方法中完成:

复制代码
    public void pollForConnections(final AzureusCore azureus_core )
    {
        //增加生命周期监听者
        azureus_core.addLifecycleListener(new AzureusCoreLifecycleAdapter()
        {
            //所有组件创建完毕
            public void componentCreated(AzureusCore core, AzureusCoreComponent    component) 
            {
                if ( component instanceof UIFunctionsSWT )
                {
                    openQueuedTorrents( azureus_core );//打开排队的种子文件列表
                }
            }
        });
        
        if ( socket != null )
        {//开启一个守护线程用于处理到来的打开种子文件列表请求
            Thread t = new AEThread("Start Server")
            {
                //runSupport是一个abstract方法,在run中调用,是实际的线程函数
                public void runSupport()
                {
                    pollForConnectionsSupport( azureus_core );
                }
            };
            t.setDaemon(true);
            t.start(); //启动线程
        }
    }
复制代码
这里线程的中断采用设置运行标志的方式,我觉得这并不是一个很好的解决方案,因为若线程要完成的工作十分耗时,则线程的中断就不能立即见效。更好的办法应该是interrupt方法和中断标志混合起来使用。
 

复制代码
private void pollForConnectionsSupport(AzureusCore azureus_core) 
  {
      bContinue = true;
      while (bContinue) 
      {
          BufferedReader br = null;
          try 
          {
              Socket sck = socket.accept();//接受一个连接请求
              String address = sck.getInetAddress().getHostAddress(); //绑定的本地IP地址
              if (address.equals("localhost") || address.equals("127.0.0.1")) 
              {
                  br = new BufferedReader(new InputStreamReader(sck.getInputStream(),Constants.DEFAULT_ENCODING));
                  String line = br.readLine();//读取一行数据          
                  if (Logger.isEnabled())
                        Logger.log(new LogEvent(LOGID, "Main::startServer: received '"+ line + "'"));
                  if (line != null)
                  {
                      String [] args = parseArgs(line);//解析请求数据
                      if (args != null && args.length > 0) 
                      {
                          String debug_str = args[0];//第一行数据仅供测试使用,故抛弃不用
                          for (int i=1; i<args.length; i++) 
                          {
                              debug_str += " ; " + args[i];
                          }
                          Logger.log(new LogEvent(LOGID, "Main::startServer: decoded to '" + debug_str + "'"));
                          processArgs(azureus_core,args); //处理解析出的种子文件列表

                      }
                  }
              }
              sck.close();
          }
          catch (Exception e) 
          {
              if(!(e instanceof SocketException))
                  Debug.printStackTrace( e );      
          }
          finally 
          {
              try 
              {
                  if (br != null)
                      br.close();
              } catch (Exception e) { /*ignore */}
          }
      }
  }
复制代码
      在种子文件列表解析成功后,对其处理要分情况考虑,若处理核心还未启动,则将其加入种子队列中排队等待,否则直接对其解析处理。

复制代码
try 
        {
            this_mon.enter();

            if (!core_started) 
            {//若核心还未启动,则进入种子队列中等待
                queued_torrents.add( new Object[]{ file_name, new Boolean( open )});//加入种子文件队列中
                queued = true;
            }
        }
        finally 
        {
          this_mon.exit();
        }
        if ( !queued )
        {//无须排队,直接进行种子文件的解析处理
            handleFile( azureus_core, file_name, open );
        }
复制代码
      具体的处理工作由handleFile完成,其中调用了openTorrent方法来打开种子文件,具体的种子文件解析见第二篇文章。

复制代码
  protected void handleFile(AzureusCore    azureus_core,String file_name,boolean open )
  {//处理种子文件
      try 
      {
          if ( open )
          {
              TorrentOpener.openTorrent(file_name);//打开种子文件
              
          }
          else
          {
              File    f = new File( file_name );
              if ( f.isDirectory())
              {
                  ShareUtils.shareDir( azureus_core, file_name );
                  
              }
              else
              {
                 ShareUtils.shareFile( azureus_core, file_name );                     
              }
          }
      } 
      catch (Throwable e)
      {
        Debug.printStackTrace(e);
      }
  }
复制代码
      当Azureus生命周期来到各个组件都创建完毕时,会通知其监听者,从而调用 openQueuedTorrents方法来处理排队中的种子文件

复制代码
protected void openQueuedTorrents(AzureusCore azureus_core )
  {
      try
      {
          this_mon.enter();
          core_started    = true;//核心启动!
      }
      finally
      {
          this_mon.exit();
      }
         
      //处理种子队列中的种子文件
      for (int i=0;i<queued_torrents.size();i++)
      {
          Object[]    entry = (Object[])queued_torrents.get(i);
          String    file_name     = (String)entry[0];//种子文件名
          boolean    open        = ((Boolean)entry[1]).booleanValue();//是否已经打开
          handleFile( azureus_core, file_name, open );
      }
  }
复制代码




本文转自Phinecos(洞庭散人)博客园博客,原文链接:http://www.cnblogs.com/phinecos/archive/2009/05/06/1451087.html,如需转载请自行联系原作者
目录
相关文章
|
5月前
muduo源码剖析之EventLoopThread
EventLoopThread类包装了一个thread类和一个EventLoop类,(one loop per thread)是封装了一个EventLoop的独立线程。
41 0
|
5月前
|
Java
muduo源码剖析之EventLoopThreadPool
EventLoopThreadPool是EventLoopThread类的线程池类封装了若干个EventLoopThread的线程池,所有者是一个外部的EventLoop。
33 0
|
存储 机器学习/深度学习 算法
源码剖析之ConcurrentHashMap
​ JDK8中ConcurrentHashMap的结构是:数组+链表+红黑树。 ​ 因为在hash冲突严重的情况下,链表的查询效率是O(n),所以jdk8中改成了单个链表的个数大于8时,数组长度小于64就扩容,数组长度大于等于64,则链表会转换为红黑树,这样以空间换时间,查询效率会变O(nlogn)。 ​ 红黑树在Node数组内部存储的不是一个TreeNode对象,而是一个TreeBin对象,TreeBin内部维持着一个红黑树。 ​ 在JDK8中ConcurrentHashMap最经点的实现是使用CAS+synchronized+volatile 来保证并发安全
107 0
源码剖析之ConcurrentHashMap
非公平锁实现原理+源码解读
非公平锁实现原理+源码解读
|
缓存 分布式计算 监控
【源码解读】| LiveListenerBus源码解读(上)
【源码解读】| LiveListenerBus源码解读
164 0
【源码解读】| LiveListenerBus源码解读(上)
|
存储 SQL 分布式计算
【源码解读】| LiveListenerBus源码解读(下)
【源码解读】| LiveListenerBus源码解读
153 0
【源码解读】| LiveListenerBus源码解读(下)