CASSINI源代码分析(3)

internal class Host : MarshalByRefObject {……}

首先,我们看到 Host 仅能够在 cassini 项目中使用,因为是 internal 的类定义。另外,继承自 MarshalByRefObject ,允许在支持远程处理的应用程序中跨应用程序域边界访问对象。我们联想到 asp.net 对于应用程序的执行方式是应用程序域为划分边界的,作为 Host 必须能够支持跨应用程序域,以便多个应用程序在其边界内执行。

先来看看其成员变量:

private bool _started; // 是否已经启动

private bool _stopped; // 是否停止了

private Server _server; // 对外交互父对象

private int _port; // 以下大致应该是一个 web server 应当具有的内部变量

private String _virtualPath;

private String _lowerCasedVirtualPath;

private String _lowerCasedVirtualPathWithTrailingSlash;

private String _physicalPath;

private String _installPath;

private String _physicalClientScriptPath;

private String _lowerCasedClientScriptPathWithTrailingSlashV10;

private String _lowerCasedClientScriptPathWithTrailingSlashV11;

private Socket _socket; // 通信用的套接字

private WaitCallback _onStart; // 回调函数

private WaitCallback _onSocketAccept;

private EventHandler _onAppDomainUnload; // 事件监控,当某个应用程序域将要退出时候发生,由 Host 类处理。

public override Object InitializeLifetimeService() {

return null; // never expire lease

} 是重载 MarshalByRefObject 的成员,正如源代码中的注释说言,通过返回 null 告诉 .net framework 永远不要将此类的实例失效过期。分布式垃圾回收负责控制服务器应用程序的生存期,并负责在它们的生存期到期时删除它们。传统上,分布式垃圾回收使用引用计数和 Ping 进行控制。这在每个对象有少数几个客户端时可以很好地工作,但在每个对象有数千个客户端时效率很低。生存期服务可采用传统分布式垃圾回收器的功能,并在客户端数目增加时能很好地扩展。

Configure 函数很值得分析,因为在 Server.CreateHost 中出现过,看看:

public void Configure(Server server, int port, String virtualPath, String physicalPath, String installPath) {

_server = server;

_port = port;

_virtualPath = virtualPath;

_lowerCasedVirtualPath = CultureInfo.InvariantCulture.TextInfo.ToLower(_virtualPath);

_lowerCasedVirtualPathWithTrailingSlash = virtualPath.EndsWith("/") ? virtualPath : virtualPath + "/";

_lowerCasedVirtualPathWithTrailingSlash = CultureInfo.InvariantCulture.TextInfo.ToLower(_lowerCasedVirtualPathWithTrailingSlash);

_physicalPath = physicalPath;

_installPath = installPath;

// 以上都是赋值和参数检验代码

_physicalClientScriptPath = installPath + "\\asp.netclientfiles\";

// 以下开始确定 asp.net 提供的客户端脚本路径

String version4 = FileVersionInfo.GetVersionInfo(typeof(HttpRuntime).Module.FullyQualifiedName).FileVersion; // 查找当前 asp.net 运行时模块的版本信息

String version3 = version4.Substring(0, version4.LastIndexOf('.'));

lowerCasedClientScriptPathWithTrailingSlashV10 = "/aspnet_client/system_web/" + version4.Replace('.', '') + "/";

lowerCasedClientScriptPathWithTrailingSlashV11 = "/aspnet_client/system_web/" + version3.Replace('.', '') + "/";

// 下面开始为套接字设置准备回调函数

_onSocketAccept = new WaitCallback(OnSocketAccept);

_onStart = new WaitCallback(OnStart);

// start watching for app domain unloading

_onAppDomainUnload = new EventHandler(OnAppDomainUnload);

Thread.GetDomain().DomainUnload += _onAppDomainUnload;

}

显然, config 函数做了以下事情:

1、 将一些配置参数传入到 host 类的内部变量

2、 确定 asp.net 提供的一些环境,譬如脚本环境、脚本存在的路径,以便 asp.net 的一些组件可以顺利执行(就象在 IIS 中一样)

3、 准备一些回调函数、事件监听函数处理发生的事件

外部使用 host 还通过 Start 过程,我们看看:

public void Start() {

if (_started) // 已经启动了,不可启动两个实例

throw new InvalidOperationException();

_socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);

_socket.Bind(new IPEndPoint(IPAddress.Any, _port));

_socket.Listen((int)SocketOptionName.MaxConnections);

_started = true;

ThreadPool.QueueUserWorkItem(_onStart);

}

该函数完成:

1、 排除启动两个实例

2、 在给定的端口上启动监听

3、 启动 _onStart 回调函数线程,运行监听套接字,但是是异步调用, start 函数完成后马上返回。

那 _onStart 函数是怎么处理的呢?

private void OnStart(Object unused) {

while (_started) {

try {

Socket socket = _socket.Accept();

ThreadPool.QueueUserWorkItem(_onSocketAccept, socket);

}

catch {

Thread.Sleep(100);

}

}

_stopped = true;

}

此线程一直进行循环,调用套接字的 Accept 函数,当一个连接接入到,马上调用一个新的线程( _onSocketAccept )和一个新的 socket ,然后产生一个新的线程来处理客户请求。遇到例外就暂停后继续 Accept 引入的套接字。一旦 _started 标志被设置 false ,则停止监听退出。

private void OnSocketAccept(Object acceptedSocket) {

Connection conn = new Connection(this, (Socket)acceptedSocket);

conn.ProcessOneRequest();

}

此处线程马上产生一个 Connection 对象,并调用 Connection 的 ProcessOneRequest 来处理。看来真正处理一个 web request 的过程在 Connection 对象内。

Published At
Categories with Web编程
Tagged with
comments powered by Disqus