国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 編程 > .NET > 正文

實現自己的ASP.NET宿主系統

2024-07-10 12:57:24
字體:
來源:轉載
供稿:網友
實現自己的asp.net宿主系統

楊山河

一、 宿主概念
托管是.net的一個很基礎的概念,所有的.net應用程序代碼要完全發揮作用需要進入托管的環境(clr --common language runtime),而這個環境實際上就是稱作宿主(host)為將要啟動的.net代碼準備的。目前來講windows系統上,能夠擔負這個重任的有3類已存程序:
1、 shell(通常是explorer),提供從用戶桌面啟動.net程序,創建一個進程,啟動此進程建立clr
2、 瀏覽器宿主(internet explorer),處理從web下載的.net代碼執行。
3、 服務器宿主(如iis的輔助進程aspnet_wp.exe)
通常來講,我們開發的asp.net的程序運行在iis的環境下(實際上由一個isapi控制啟動clr),但實際上asp.net程序可以擺脫iis單獨在任何托管環境下運行。本文討論了asp.net程序如何在自定義的環境中啟動,希望有助于我們了解asp.net的執行原理,同時使我們開發的asp.net能夠在任何.net環境下執行,不管是服務器操作系統還是普通的桌面操作系統。

二、 iis宿主中asp.net的執行分析
關于iis中asp.net的執行細節,很多文章做了詳盡權威的分析,本文不打算贅述,在此給出一些參考:
http://www.yesky.com/softchannel/72342380468043776/20030924/1731387.shtml
http://chs.gotdotnet.com/quickstart/aspplus/doc/procmodel.aspx
這些文章大致重點分析了:宿主環將如何啟動、asp.net應用程序如何產生程序集、如何加載,同宿主的交互等細節。

三、 構造自己的asp.net宿主程序
asp.net是作為微軟asp的替代技術出現的,所以我們重點討論如何通過web方式應用asp.net(顯然還有其他方式),具體就是:我們用.net平臺的語言編寫一個控制臺程序,這個程序啟動一個asp.net應用環境,執行關于aspx的請求。具體來講,需要做以下工作:
1、實現一個web server,監聽所有的web請求,實現http web hosting
2、啟動一個應用程序域,創建一個asp.net的applicationhost,建立一個asp.net的應用程序域,另外還建立一個httpworkerrequest的具體實現類,該類可以處理aspx請求,編譯aspx頁,編譯后的托管代碼緩存入當前應用程序域,然后執行代碼,得到執行結果。建議在繼續閱讀下文前,仔細翻查msdn中的關于這兩個類得參考說明。
system.web.hosting.applicationhost類用于建立一個獨立的應用程序域。當然不是普通的應用程序域,而是為asp.net建立執行環境,準備需要的空間、數據結構等。僅有一個靜態方法static object createapplicationhost(
type host //具體的用戶實現類,就是asp.net應用域需要加載的類
string virtualdir, //此應用域在整個web中的執行目錄,虛擬目錄
string physicaldir //對應的物理目錄
);
而其中的host 參數指向一個具體的類,由于該類實際上屬于兩個應用域之間的聯系類,在兩個應用程序域之間編組傳遞數據,所以必須要繼承自marshalbyrefobject,以允許在支持應用程序中跨應用程序域邊界訪問(至于為什么,建議翻查參考3)。
可以看到,我們需要啟動兩個應用程序域(web server功能應用程序域和asp.net 應用程序域),而這兩個(應用程序)域之間通過跨(應用程序)域的流對象引用來實現,使得在asp.net域中執行的結果可以通過web server域返回給請求者。
可以大致下圖表達
執行asp.net的web服務器端










web客戶端

代碼實現分析:
using system;
using system.web ;
using system.web.hosting;
using system.io;
using system.net;
using system.net.sockets ;
using system.text ;
using system.threading ;

namespace myiis
{
class asphostserver
{
[stathread]
static void main(string[] args)
{
//創建并啟動服務器
myserver myserver=new myserver(“/”, ”c://inetpub//wwwroot//myweb”);
}
}

class myserver //處理http協議的服務器類
{
private aspdotnethost aspnethost; //asp.net host的實例
private tcplistener mytcp; //web監聽套接字
bool bsvcrunning=true; //服務是否運行指示
filestream fs; //處理http請求的普通文本要求

public myserver(string virtualdir ,vstring realpath)
{//在構造函數中啟動web監聽服務
try
{
mytcp=new tcplistener(8001);
mytcp.start(); //啟動在8001端口的監聽
console.writeline("服務啟動...");
//利用createapplicationhost方法建立一個獨立的應用程序域執行asp.net程序
aspnethost = ( aspdotnethost )applicationhost.createapplicationhost
( typeof( aspdotnethost ) , virtualdir , realpath);
thread t=new thread(new threadstart(mainsvcthread));
t.start(); //服務線程啟動 負責處理每一個客戶端的請求
}
catch(nullreferenceexception)
{
console.writeline("nullreferenceexception throwed!") ;
}
}

public void mainsvcthread() //asp.net host的web服務器的主要服務線程
{
int s=0;
string strrequest; //請求信息
string strdir; //請求的目錄
string strrequestfile; //請求的文件名
string strerr=""; //錯誤信息
string strrealdir; //實際目錄
string strwebroot=rpath; //應用根目錄
string strrealfile=""; //正在請求的文件的磁盤路徑
string strresponse=""; //回應響應緩沖區
string strmsg=""; //格式化響應信息
byte[] bs; //輸出字節緩沖區

while(bsvcrunning)
{
socket sck=mytcp.acceptsocket(); //每個請求到來
if(sck.connected)
{
console.writeline("client {0} connected!",sck.remoteendpoint);
byte[] brecv=new byte[1024]; //緩沖區
int l=sck.receive(brecv,brecv.length,0);
string strbuf=encoding.default.getstring(brecv); //轉換成字符串,便于分析
s=strbuf.indexof("http",1);
string httpver=strbuf.substring(s,8); // http/1.1 之類的
strrequest=strbuf.substring(0,s-1);
strrequest.replace("//","/");
if((strrequest.indexof(".")<1) && (!strrequest.endswith("/")))
{
strrequest += "/";
}
s=strrequest.lastindexof("/")+1;
strrequestfile = strrequest.substring(s); strdir=strrequest.substring(strrequest.indexof("/"),strrequest.lastindexof("/")-3); //取得訪問的url
if(strdir=="/")
{
strrealdir=strwebroot;
}
else
{
strdir=strdir.replace("/","//");
strrealdir=strwebroot + strdir;
}
console.writeline("client request dir: {0}" , strrealdir);
if(strrequestfile.length==0)
{
strrequestfile="default.htm"; //缺省文檔
}
int itotlabytes=0; //總計需要輸出的字節
strresponse=""; //輸出內容
strrealfile = strrealdir +"//"+ strrequestfile;
if(strrealfile.endswith(".aspx")) //這里有bug!!
{
string output="";
//注意我下面的語句們給host對象processrequest方法傳遞了一個ref類型的參數,
//aspnethost會從asp.net的執行應用程序域執行一個請求后返回流給當前web server所在的域,這實際上發生了一個域間的調用
aspnethost.processrequest (strrequestfile, ref output);//轉換成字節流
bs=system.text.encoding.default.getbytes (output);
itotlabytes=bs.length ; //調用套接字將執行結果返回
writeheader(httpver,"text/html",itotlabytes,"200 ok",ref sck);
flushbuf(bs,ref sck);
}
else
{try
{
fs=new filestream( strrealfile,filemode.open,fileaccess.read,fileshare.read );
binaryreader reader=new binaryreader(fs); //讀取
bs=new byte[fs.length ];
int rb;
while((rb=reader.read(bs,0,bs.length ))!=0)
{
strresponse =strresponse +encoding.default.getstring(bs,0,rb);
itotlabytes =itotlabytes+rb;
}
reader.close();
fs.close();
writeheader(httpver,"text/html",itotlabytes,"200 ok",ref sck);
flushbuf(bs,ref sck);
}
catch(system.io.filenotfoundexception )
{//假設找不到文件,報告404 writeheader(httpver,"text/html",itotlabytes,"404 ok",ref sck);
}
}
}
sck.close(); //http請求結束
}
}

// writeheader想客戶端發送http頭
public void writeheader(string ver,string mime,int len,string statucode,ref socket sck) {
string buf="";

if(mime.length ==0)
{
mime="text/html";

buf=buf+ver+ statucode + "/r/n";
buf=buf+"server:myiis"+"/r/n";
buf=buf+"content-type:"+mime +"/r/n";
buf=buf+"accept-rabges:bytes"+"/r/n";
buf=buf+"content-length:"+ len +"/r/n/r/n";
byte[] bs=encoding.default.getbytes(buf);
flushbuf(bs,ref sck);
}
}

// flushbuf刷新向客戶發送信息緩沖區
public void flushbuf(byte[] bs,ref socket sck)
{
int inum=0;
try
{
if(sck.connected)
{
if((inum=sck.send(bs,bs.length ,0))==-1)
{
console.writeline("flush err:send data err");
}
else
{
console.writeline("send bytes :{0}",inum);
}
}
else
{
console.writeline("client diconnectioned!");
}
}
catch(exception e)
{
console.writeline("error:{0}",e);
}
}
}

// aspdotnethost類實例需要跨越兩個應用程序域,所以繼承自marshalbyrefobject
class aspdotnethost:marshalbyrefobject
{
public void processrequest( string filename ,ref string output)
{
memorystream ms=new memorystream(); //內存流,當然為了速度
streamwriter sw = new streamwriter(ms); //輸出
sw.autoflush = true; //設為自動刷新 /先構造一個httpworkrequest請求類,以便asp.net能夠分析獲取請求信息,同時傳入一個輸出流對象供asp.net執行期間返回html流
httpworkerrequest worker = new simpleworkerrequest( filename, "" ,sw) ; // 調度某個頁,這里面的包含很多細節,后面分析
httpruntime.processrequest( worker ) ;
streamreader sr= new streamreader(ms); //準備從內存流中讀取
ms.position =0; //移動指針到頭
output = sr.readtoend();
}
}
}
httpruntime.processrequest( worker ) ;包括了那些細節呢?大體上如下:
1、首先,worker對象傳入給asp.net的應用程序域,告知發生了對于哪一個aspx文件的請求,以及當前目錄是什么,如果在執行期間發生的輸出內容應該寫到哪里(sw對象)。這發生一個由web server當前應用程序域到我們自己建立的asp.net應用程序域的跨(應用程序)域調用,還可能由于是第一次訪問,會發生了全局事件、或者session事件等。
2、asp.net的應用程序域會檢測請求的aspx文件是否存在,不存在,就報錯;如果存在還要看看代碼緩存中是否存在上次編譯的代碼,如果存在且asp.net檢測到不需要重新編譯,會直接執行緩存中的代碼;如果不存在或者代碼過期需要重新編譯,就需要讀取aspx文件,編譯成.net的代碼,存入緩存。可能有些頁存在代碼和模板分離成多個文件,甚至包括一些資源文件,這些都需要讀取后編譯成.net的虛擬機代碼,然后在托管環境里執行。
3、執行asp.net的編譯代碼緩存中的代碼,輸出數據利用sw對象輸出。
當然,根據不同的配置,還有很多方法的調用/事件的發生等細節不同。
如何調試運行以上程序,觀察結果呢?
建立一個控制臺類型工程,將上述代碼錄入后編譯,將得到的程序拷貝在作為站點應用起始目錄(譬如c:/inetpub/wwwroot/myweb)的bin子目錄下,然后啟動,這樣在其中創建asp.net應用程序域才不會因為程序集加載失敗而出錯。建立一個asp.net工程在目錄下,添加default.htm文件和測試用的test.aspx,加入.net執行代碼,然后啟動ie,在地址欄分別輸入:http://127.0.0.1:8001/default.htm http://127.0.0.1:8001/test.aspx感受一下執行過程。甚至你可以建立的工程中設定斷點之類,仔細調試和觀察其中的細節。親手試一試吧,一定有收獲的!

四、 自己構造asp.net宿主的意義
費了半天勁搞自己的asp.net宿主,對于我們有何意義呢?
首先,是大致從代碼級清楚分析asp.net執行細節,自己學習了解執行細節,除了可以在出現asp.net故障可以進行精確定位和排除外,還可以幫助我們在寫asp.net應用程序時寫出更有效率和健壯的代碼。
其次,我們可以提供一個思路,可以將我們的asp.net程序運行于低配置機器上,脫離iis。asp.net的“原配”宿主iis需要運行在server os上,要知道在安全專家眼中,iis可是大隱患的源頭之一。我們可以將很多傳統程序利用asp.net編寫,但脫離iis獨立執行,譬如在win98系統上執行asp.net。web server和asp.net都在托管環境中執行,相比較isapi建立宿主然后執行,除提高效率外,還可以使用.net平臺提供的豐富管理調控功能,寫b/s程序更接近傳統程序編寫方式,這對于程序員來講都是效率(編寫代碼的效率和執行效果效率)的保證。
另外,對于采用asp.net做的項目,大家可以很方便進行開發調試、運行維護、安裝。即使是普通桌面程序,我們也可以通過類似制作網頁的方式編寫這些界面和代碼,然后獨立建立類似本例中的host環境,根據用戶交互請求加載執行某些頁面,然后將界面在客戶端通過相關組件顯示出來。你可以通過此獲得asp.net的即時編譯功能和asp.net宿主托管環境,大量可自由使用的api,便于開發、安裝、維護。畢竟,托管環境幾乎準備了您需要的一切功能。

五、 參考資料
1、.net msdn
2、清華大學出版社《.net網絡高級編程》andrew krowczyk viond kumar原著
3、清華大學出版社《.net框架程序設計(修訂版)》jsfftry richter著



發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 商洛市| 阜城县| 抚宁县| 浮山县| 游戏| 托克托县| 瓦房店市| 曲阳县| 鄂尔多斯市| 兴安盟| 乐清市| 绥江县| 阳谷县| 海兴县| 固镇县| 辛集市| 洪雅县| 镇雄县| 棋牌| 镇宁| 嘉义市| 马公市| 长武县| 兴仁县| 柘荣县| 虹口区| 宁强县| 寿宁县| 榆中县| 龙口市| 宁蒗| 大同市| 石城县| 岑巩县| 子洲县| 绿春县| 元阳县| 绥化市| 万全县| 新河县| 张掖市|