Azureus源码剖析(三)
接着第一篇的工作,本篇继续分析种子文件监听服务器的实现细节。
先简单描述下其工作流程,首先服务器在6880端口处开启一个套接字监听,然后开启一个守护线程用于处理到来的“打开种子文件列表”请求,在这个服务线程中不断循环读取来自客户的请求,对torrent文件列表进行解析。如果此时Azureus的各个组件都已经创建完毕,则说明Azureus的核心处理组件可用,则直接对torrent文件列表进行处理,否则,先将torrent文件列表加入种子文件队列中,等到各个组件创建完毕后再来处理种子队列中的各个种子文件。
下面来查看其源代码,先看其成员变量:
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;//核心处理组件是否已经启动
在其构造函数中完成服务器的创建和启动:
state = STATE_LISTENING;//设置服务器状态为“监听”
而在pollForConnections中为Azureus添加了一个生命周期监听器,这样当其所有组件完成时就会通知此监听器,而后者会调用openQueuedTorrents方法,对待解析种子文件队列中排队的种子文件进行处理,此外,这个方法中还创建了一个守护线程用于处理到来的打开种子文件列表请求,实际的处理工作在pollForConnectionsSupport方法中完成:
{
//增加生命周期监听者
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方法和中断标志混合起来使用。
{
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 */}
}
}
}
在种子文件列表解析成功后,对其处理要分情况考虑,若处理核心还未启动,则将其加入种子队列中排队等待,否则直接对其解析处理。
{
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方法来打开种子文件,具体的种子文件解析见第二篇文章。
{//处理种子文件
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方法来处理排队中的种子文件
{
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 );
}
}
作者:洞庭散人
出处:http://phinecos.cnblogs.com/
posted on 2009-05-06 22:50 Phinecos(洞庭散人) 阅读(1352) 评论(0) 编辑 收藏 举报