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对象内。