天天看點

Asp.NET 的Session實作原理

Session天天用,但是你真的了解了麼?

今天遇到了這個問題,于是研究了一下。要解決這個問題,首先就要明白一些Session的機理。Session在伺服器是以散清單形式存在的,我們都知道Session是會話級的,每個使用者通路都會生成一個Session。那麼伺服器是怎麼區分不同使用者的Session?又是怎麼将不同使用者的Session與不同的使用者綁定的呢?下面我們來研究一下,以下純屬我個人的了解,如有錯誤請指證。

Session在伺服器端是以散清單的形式存在的,區分每一個Session是通過SessionID來實作的,是以可以說這個SessionID是一個Key是一個全局唯一的值。我們可以通過ASP.NET來列印出SessionID,如下代碼:

protected void Page_Load(object sender, EventArgs e)

{

Response.Write(Session.SessionID.ToString());

}

這樣我們就得到了這樣的值:0julmoedn0kz3gyfnr1vksv0,有點像是GUID,就算不是算法也都是類似的,主要就是為了保證全局唯一性。這樣就達到了區分不同使用者的Session的目的。接下來還有第二個問題,那就是SessionID有了,但是它又是怎麼和相應的通路者(使用者)綁定的呢?比如說使用者A通路維護了自己的SessionID,使用者B通路也維護了自己的SessionID。我們都知道web是基于http無連結的,他們又是怎麼做到的呢?沒錯,答案就是在用戶端存儲了自己的SessionID。浏覽器存儲SessionID有兩種方式,一種就是利用Cookies;還有一種就是利用url參數(這種我們不常用,很不友好)。

話題說到Cookies上來了,怎麼的?沒想到Session和Cookies還有這樣的關系吧?(很多人知道,别BS我)沒錯,當我們請求一個URL時候,伺服器會生成一個全局的SessionID,并且把這個值以Cookies的形式儲存在用戶端也就是浏覽器(這裡暫不讨論url方式)。這樣當使用者再去請求的時候,在http頭把這個SessionID的Cookie發到伺服器端,伺服器就去找這個SessionID,如果找到了。就證明這個使用者的狀态是存在的。

Response.Cookies["MyCook"].Domain = ".local.com";

這樣,我們所有的二級域全部是認這一個主域的,比如a.local.com;b.local.com;user.local.com等等。有了這個認識,我想大家心裡也有數了,該怎麼怎麼做,但是現在問題是用來生成SessionID的方法是ASP.NET自動實作的,我們又怎麼去幹涉它呢?這是這樣做的,不主動幹涉它,但是我可以操作它的Cookies啊。接下來我們就研究ASP.NET存SessionID的Cooike的名字是什麼。經過網上很容易就查找到了,名字是:ASP.NET_SessionId,這個就是SessionId的Cookies名字。我們可以在Session_Start中這樣寫:

代碼 

protected void Session_Start(object sender, EventArgs e)

Response.Cookies["ASP.NET_SessionId"].Value = Session.SessionID.ToString();

Response.Cookies["ASP.NET_SessionId"].Domain = ".local.com";

代碼的意思是每次會話開始的時候,我都把ASP.NET_SessionId這個Cookie重寫成我們已有的SessionID,并且把這個Cookie的domain指定為父域,比如:.local.com,這樣就可以實作跨子域的Session共享了。怎麼樣很簡單吧?

我們還有一個外題問題,就是用戶端儲存的問題解決了,但是伺服器端的Session怎麼辦?一般情況下我們不同的子域做的是指向不同的伺服器的,比如user.local.com 專門一台伺服器,yellow.local.com專門一台伺服器。這時它們别說是程序了,連實體上都不是一個了。Session怎麼共享?這時就用到另一個方法了,我們預設的Session是存儲在asp.net程序中的,這樣沒法互相通路,如下面所示:

<sessionState mode="InProc" />

我們可以修改為State Server方式,這是一個單獨的服務可以用來存儲ASP.NET Session的,它支援分布式遠端主機的,這樣我們可以用一台伺服器來提供Session服務,如下所示:

<sessionState mode="StateServer" stateConnectionString="tcpip=127.0.0.1:42424" timeout="30" />

這樣,就完全實作了不同子域的Session共享了。

前面說到Url儲存SessionId的方式,由于不常用,給大家示範一下,如下配置就可以了:

<sessionState mode="StateServer" stateConnectionString="tcpip=127.0.0.1:42424" timeout="30" cookieless="true" />

本文轉自linzheng 51CTO部落格,原文連結:http://blog.51cto.com/linzheng/1081547