asp.net中session工作階段狀態詳解

來源:互聯網
上載者:User

Session又稱為工作階段狀態,是Web系統中最常用的狀態,用於維護和當前瀏覽器執行個體相關的一些資訊。舉個例子來說,我們可以把已登入使用者的使用者名稱放在Session中,這樣就能通過判斷Session中的某個Key來判斷使用者是否登入,如果登入的話使用者名稱又是多少。

我們知 道,Session對於每一個用戶端(或者說瀏覽器執行個體)是“人手一份”,使用者首次與Web伺服器建立串連的時候,伺服器會給使用者分發一個 SessionID作為標識。SessionID是一個由24個字元組成的隨機字串。使用者每次提交頁面,瀏覽器都會把這個SessionID包含在 HTTP頭中提交給Web伺服器,這樣Web伺服器就能區分當前請求頁面的是哪一個用戶端。那麼,ASP.NET 2.0提供了哪些儲存SessionID的模式呢:

·      Cookie(預設)。如果用戶端禁止了Cookie的使用,Session也將失效。

·      URL。Cookie是否開啟不影響Session使用,缺點是不能再使用絕對連結了。

前面說了SessionID可以儲存在用戶端的Cookie或者URL中,那麼Session真正的內容儲存在哪裡呢?ASP.NET 2.0對於Session內容的儲存也提供了多種模式。

·      InProc(預設)。Session儲存在IIS進程中(Web伺服器記憶體)。

·      StateServer。Session儲存在獨立的Windows服務進程中(可以不是Web伺服器)。

·      SqlServer。Session儲存在SqlServer資料庫的表中(SqlServer伺服器)。

雖然 InProc模式的Session直接儲存在Web伺服器IIS進程中,速度比較快,但是每次重新啟動IIS都會導致Session丟失。利用後兩種模式,我們就完全可以把Session從Web伺服器中獨立出來,從而減輕Web伺服器的壓力,同時減少Session丟失的機率。

因此,SessionID儲存在用戶端(可以是Cookie或者URL),其他都儲存在服務端(可以是IIS進程、獨立的Windows服務進程或者SQL Server資料庫中)。

12.3.2  Session的使用
讓我們先來實踐一下如何使用Session,進而回答第二個問題:Session儲存的類型限制。Session不需要進行任何配置就可以使用(預設是InProc模式並且依賴Cookie)。首先,在頁面上建立兩個按鈕。

 

在btn_WriteSession按鈕的Click事件處理方法中,寫入兩個Session,一個是簡單的字串,另外一個是自訂的類。

 代碼如下 複製代碼

protected void btn_WriteSession_Click(object sender, EventArgs e)

{

    Session["SimpleString"] = "編程快樂";

    MyUser user = new MyUser();

    user.sUserName = "小朱";

    user.iAage = 24;

    Session["CustomClass"] = user;

}

Session的使用非常簡單,直接對某個Key的Session進行賦值即可。自訂類MyUser如下:

 代碼如下 複製代碼

class MyUser

{

    public string sUserName;

    public int iAage;

    public override string ToString()

    {

        return string.Format("姓名:{0},年齡:{1}", sUserName, iAage);

    }

}

在這裡,我們覆寫了ToString()方法直接返回執行個體的一些資訊。然後,雙擊btn_ReadSession按鈕來實現從Session中讀取資料的代碼:

 代碼如下 複製代碼

protected void btn_ReadSession_Click(object sender, EventArgs e)

{

    if (Session["SimpleString"]==null)

    {

        Response.Write("讀取簡單字串失敗
");

    }

    else

    {

        string s=Session["SimpleString"].ToString();

        Response.Write(s + "
");

    }

    if (Session["CustomClass"]==null)

    {

        Response.Write("讀取簡單自訂類失敗
");

    }

    else

    {

        MyUser user=Session["CustomClass"] as MyUser;

        Response.Write(user.ToString()+"
");

    }

}

在每次讀取Session的值以前請務必先判斷Session是否為空白,否則很有可能出現“未將對象引用設定到對象的執行個體”的異常。我們看到,從Session 中讀出的資料都是object類型的,我們需要進行類型轉化後才能使用。開啟頁面,先單擊寫入Session按鈕,再單擊讀取Session按鈕,頁面輸出如    圖12-1所示。

 

12.3.3  把Session儲存在獨立的進程中
由此看來,Session能儲存任意對象,是這樣嗎?現在得出這個結論還太早了一點,因為我們並沒有實踐過StateServer和SqlServer模式的Session。要把Session儲存在Windows服務進程中需要進行以下幾個步驟。

n  第1步是開啟狀態服務。依次開啟“控制台”→“管理工具”→“服務”命令,找到ASP.NET狀態服務一項,按右鍵服務選擇啟動,如圖12-2所示。

 

圖12-2  啟動ASP.NET狀態服務

n  如果你正式決定使用狀態服務儲存Session前,別忘記修改服務為自啟動(在作業系統重啟後服務能自己啟動)以免忘記啟動服務而造成網站Session不能使用,如圖12-3所示,雙擊服務把服務的啟動類型設定為自動。

 

圖12-3  修改服務啟動類型為自動

服務正常啟動後可以觀察工作管理員的進程頁,其中的aspnet_state.exe進程就是狀態服務進程,如圖12-4所示。

 

圖12-4  觀察工作管理員的進程頁

n  第2步,在system.web節點中加入:


stateNetworkTimeout="20">

n  stateConnectionString表示狀態伺服器的通訊地址(IP:服務連接埠號碼)。由於我們現在在本機進行測試,這裡設定成本機地址127.0.0.1。狀態服務預設的監聽連接埠為42422。當然,您也可以通過修改註冊表來修改狀態服務的連接埠號碼。

n  1.在運行中輸入regedit啟動登錄編輯程式。

n  2.依次開啟HKEY_LOCAL_MACHINESYSTEMCurrentControlSetServicesaspnet_stateParameters節點,雙擊Port選項,如圖12-5所示。

選擇基數為十進位,然後輸入一個連接埠號碼即可。stateNetworkTimeout屬性工作表示從狀態伺服器請求Session資料最長的時間,預設為10秒,如果網路連接不是很好,請把這個數字適當設定得大一點。

n  第3步開啟頁面,單擊“寫入Session”按鈕,系統會報錯,如圖12-6所示。

   

        圖12-5  修改狀態服務連接埠號碼            圖12-6  向StateServer預設的Session中寫入自訂類出錯

提示已經說得很清楚了,只有把對象標註為可序列化後才能在服務中進行儲存。什麼是序列化呢?序列化是指將對象執行個體的狀態儲存到儲存媒體的過程。在此過程中,先將對象的公用欄位和私人欄位以及類的名稱轉換為位元組流,然後再把位元組流寫入資料流。在隨後對對象進行還原序列化時,將建立出與原對象完全相同的副本。要使一個類可序列化,最簡單的方法是使用 Serializable 屬性對它進行標記。

 代碼如下 複製代碼

[Serializable]

class MyUser

{

    public string sUserName;

    public int iAage;

    public override string ToString()

    {

        return string.Format("姓名:{0},年齡:{1}", sUserName, iAage);

    }

}

n  第4步現在重新開啟頁面進行測試,得到的結果和使用InProc模式是一樣的。

12.3.4  把Session儲存在資料庫中
要把Session儲存在SqlServer中,基本上也是這麼幾個步驟。

n  1.在命令列視窗輸入cmd並在命令列中運行如下命令。

C:WINDOWSMicrosoft.NETFrameworkv2.0.50727aspnet_regsql.exe -S .SqlExpress -E –ssadd

其中 C:Windows用你自己Windows的目錄代替,v2.0.50727用你安裝的2.0架構的版本號碼代替。-S指定SqlServer伺服器位址,-E表示採用信任連接,-ssadd表示為SqlServer伺服器添加狀態服務的支援。操作結束後,你可以使用IDE的伺服器總管串連 SqlExpress資料庫,可以看到多了一個ASPState資料庫,但是奇怪的是資料庫中沒有任何錶卻有很多預存程序,如圖12-7所示。

其實,所有Session的資料都存放在了tempdb資料庫內,如圖12-8所示。

           

    圖12-7  使用伺服器總管瀏覽ASPState資料庫      圖12-8  存放Session資料的tempdb資料庫

其實,aspnet_regsql.exe有一個-sstype參數可以用來指定Session的內容和操作的預存程序存放的表。由於篇幅關係,在這裡就不詳細介紹了,讀者可以使用aspnet_regsql.exe/?來瀏覽程式詳細的使用方式。

n  2.開啟Web.config檔案,修改前面建立的sessionState節點。


為sqlConnectionString 屬性指定以前一直用的連接字串,唯一不同的是不需要再指定資料表的名字了。sqlCommandTimeout屬性工作表示允許執行Sql命令最長的時間,預設為30秒,可以根據自己的需要適當調整這個數字。最後,重新開啟頁面進行測試,得到的結果和使用InProc模式是一樣的(同樣你需要確保在自訂類前標註了[Serializable]),不過我們能感到速度有些慢了,畢竟資料是從資料庫中進行讀取或儲存的,而且在使用前還需要經過序列化和還原序列化操作。

因此Session能儲存的類型為: 對於InProc模式是一切類型,而對於StateServer和SqlServer模式是一切可以序列化的類型。

12.3.5  Session的使用範圍與大小限制
那麼, 工作階段狀態使用的範圍和大小限制又是怎麼樣的呢?我們可以分析一下圖12-8,系統使用兩個表來儲存Session的狀態。其中有一個 ASPStateTempApplication表,用來儲存Session所在的應用程式,一定程度上反映了Session是不能跨應用程式的。舉例來說,我們在電腦上建立了兩個網站,同時都使用Session[“UserName”]來儲存登入的使用者名稱,一個網站的使用者登入後,另一個網站直接存取 Session[“UserName”]是取不到任何值的。那麼,Session是否可以跨使用者呢?通過前面的分析我們知道,肯定是不行的, Session通過SessionID來區分使用者,一般來說SessionID是不可能出現重複的現象,也就是說Session一般是不會“串號”的。既然頁面每次提交的時候都會附加上目前使用者的SessionID,那麼Session應該是可以跨頁面的,也就是說一個網站中所有的頁面都使用同一份 Session。你可以自己來做個實驗,請讀者開啟剛才那個頁面,然後按Ctrl+N按鍵組合再開啟第二個同樣的頁面,單擊第一個頁面中的“寫入 Session”按鈕,單擊第二個頁面中的“讀取Session”按鈕,可以發現Session的值被正確讀出了。第三個問題的答案有了。

·      Session狀態使用的範圍:使用同一個用戶端(瀏覽器執行個體)訪問同一個應用程式的所有頁面。

我們再來做一個實驗,看看Session的容量有多大,在測試以前請修改Web.config,把Session設定為StateServer模式。然後,把寫入Session的代碼修改成如下(別忘記using System.Data.SqlCient):

 代碼如下 複製代碼

DataSet ds = new DataSet();

using (SqlConnection conn = new SqlConnection(@"server=(local)SQLEXPRESS;database=Forum;

Trusted_Connection=True"))

{

    SqlDataAdapter da = new SqlDataAdapter("select * from tbUser;select * from tbBoard;

    select * from tbTopic;", conn);

    da.Fill(ds);

}

ArrayList al = new ArrayList();

for(int i = 0;i<10000000;i++)

    al.Add(ds);

Session["LargeData"] = al;

我們把包含三個表的DataSet重複加入ArrayList中1000萬次。由於這些表幾乎每個表只有幾條記錄,這樣可以類比大資料量的情況。啟動頁面,單擊“寫入Session”按鈕後可以發現,Windows服務進程一下子佔用了多達70MB的記憶體,如圖12-9所示。

 

圖12-9  把大量資料存放到Session中

Session 對於網站和使用者是獨立的,試想一下,如果伺服器上有兩個網站,每個網站的線上人數是100人,那麼佔用記憶體就要14G。是不是很恐怖的數字?因此,雖然 Session的大小沒有限制,但是我們千萬不能濫用Session。筆者推薦你在Session中儲存少於100K的資料。

·      如果你使用InProc模式的Session,儲存過多的資料會導致IIS進程被回收,引發Session不斷丟失。

·      如果你使用StateServer儲存Session,那麼資料在存入Session以前需要進行序列化,序列化會消耗大量的CPU資源。

·      如果你使用SqlServer模式的Session,資料不但要序列化而且還是儲存在磁碟上,更不適合儲存大量資料。

12.3.6  Session的生命週期
在瞭解了Session中儲存的資料無大小限制後,我們可能要更多地關心Session的生命週期了。我們已經知道,Session是在使用者第一次訪問網站的時候建立的,那麼Session是什麼時候銷毀的呢?Session使用一種平滑逾時的技術來控制何時銷毀Session。預設情況下,Session 的逾時時間(Timeout)是20分鐘,使用者保持連續20分鐘不訪問網站,則Session被收回,如果在這20分鐘內使用者又訪問了一次頁面,那麼20 分鐘就重新計時了,也就是說,這個逾時是連續不訪問的逾時時間,而不是第一次訪問後20分鐘必過時。這個逾時時間同樣也可以通過調整Web.config 檔案進行修改:


當然你也可以在程式中進行設定:

Session.Timeout = "30";

一旦Session逾時,Session中的資料將被回收,如果再使用Session系統,將給你分配一個新的SessionID。本節一開始我們就介紹了可以在URL中儲存SessionID,現在請你配置Web.config檔案,設定Session逾時時間為1分鐘,SessionID在URl中存放。開啟頁面後單擊“寫入Session”按鈕,過1分鐘再次單擊按鈕並觀察SessionID是否變化。


如圖12-10所示,SessionID的確發生了變化。

 

 

圖12-10  逾時後SessionID發生變化

不過,你可別太相信Session的Timeout屬性,如果你把它設定為24小時,則很難相信24小時之後使用者的Session還在。Session是否存在,不僅僅依賴於Timeout屬性,以下的情況都可能引起Session丟失(所謂丟失就是在逾時以前原來的Session無效)。

·      bin目錄中的檔案被改寫。asp.net有一種機制,為了保證dll重新編譯之後,系統正常運行,它會重新啟動一次網站進程,這時就會導致 Session丟失,所以如果有access資料庫位於bin目錄,或者有其他檔案被系統改寫,就會導致Session丟失。

·      SessionID丟失或者無效。如果你在URL中儲存SessionID,但是使用了絕對位址重新導向網站導致URL中的SessionID丟失,那麼原來的Session將失效。如果你在Cookie中儲存SessionID,那麼用戶端禁用Cookie或者Cookie達到了IE中Cookie數量的限制(每個域20個),那麼Session將無效。

·      如果使用InProc的Session,那麼IIS重啟將會丟失Session。同理,如果使用StateServer的Session,伺服器重新啟動Session也會丟失。

一般來說,如果在IIS中儲存Session而且Session的Timeout設定得比較長,再加上Session中儲存大量的資料,非常容易發生Session丟失的問題。

最後, Session的安全性怎麼樣呢?我們知道,Session中只有SessionID是儲存在用戶端的,並且在頁面每次提交的過程中加入HTTP頭髮送給伺服器。SessionID只是一個識別符,沒有任何內容,真正的內容是儲存在伺服器上的。總的來說安全性還是可以的,不過筆者建議你不要使用 cookieless和SqlServer模式的Session。把SessionID暴露在URL中,把內容儲存在資料庫中可能會發生攻擊隱患。

12.3.7  遍曆與銷毀Session
Session雖然很方便,但是要用好Session還需要自己不斷實踐,根據自己網站的特點靈活使用各種模式的Session。關於使用程式訪問Session,筆者還想補充兩點。

·      如何遍曆當前的Session集合。

 代碼如下 複製代碼

System.Collections.IEnumerator SessionEnum = Session.Keys.GetEnumerator();

while (SessionEnum.MoveNext())

{

    Response.Write(Session[SessionEnum.Current.ToString()].ToString() + "
");

}

對於我們這個例子,輸出和圖12-1一樣。如果你僅僅為了監視Session,也可以通過trace來獲得詳細資料。在Web.config的system.Web節點中添加:


開啟頁面後單擊“寫入Session”按鈕,頁面顯示如圖12-11所示。

 

圖12-11  使用trace觀察工作階段狀態

·      如何立刻讓Session失效。比如使用者退出系統後,Session中儲存的所有資料全部失效,可以使用以下代碼來讓Session失效。

Session.Abandon();

12.3.8  Session的常見問題與總結
Session的基本知識就介紹到這裡,現在再回頭看第一節中的幾個問題,你是否都能回答了呢?為了強化大家的概念,筆者就三種模式的Session進行了一個比較(假設都使用Cookie來儲存SessionID)。

表12.1  三種模式的Session比較


 InProc
 StateServer
 SQLServer
 
儲存物理位置
 IIS進程(記憶體)
 Windows服務進程(記憶體)
 SQLServer資料庫(磁碟)
 
儲存類型限制
 無限制
 可以序列化的類型
 可以序列化的類型
 
儲存大小限制
 無限制
 
使用範圍
 當前請求上下文,對於每個使用者獨立
 
生命週期
 第一次訪問網站的時候建立Session逾時後銷毀
 
優點
 效能比較高
 Session不依賴Web伺服器,不容易丟失
 
缺點
 容易丟失
 序列化與還原序列化消耗CPU資源
 序列化與還原序列化消耗CPU資源,從磁碟讀取Session比較慢
 
使用原則
 不要存放大量資料
 

在使用Session的過程中你可能還會遇到很多奇怪的問題,結束本節之前筆者列出了幾條常見的FAQ,供大家參考:

·      為什麼每次請求的SessionID都不相同?

n  可能是沒有在Session裡面儲存任何資訊引起的,即程式中任何地方都沒有使用Session。只有在Session中儲存了內容後,Session才會和瀏覽器進行關聯,此時的SessionID將不會再變化。

·      為什麼當我設定cookieless為true後,在重新導向的時候會丟失Session?

n  當使用cookieless時,你必須使用相對路徑替換程式中的絕對路徑,如果使用絕對路徑,ASP.NET將無法在URL中儲存SessionID。

·      有辦法知道應用程式的Session在運行時佔用了多少記憶體嗎?

n  沒有辦法,你可以通過察IIS進程(InProc模式)或者aspnet_state進程(StateServer模式)大致估計。

·      有沒有可能知道整個網站使用Session的使用者列表?

n  對於InProc模式和StateServer模式很難,對於SqlServer模式你可以查詢儲存Session的表進行嘗試。

·      當頁面中設了frameset,發現在每個frame中顯示頁面的SessionID在第一次請求時都不相同,為什嗎?

n  原因是你的frameset是放在一個HTML頁面上而不是ASPX頁面。在一般情況下,如果frameset是aspx頁面,當你請求頁面時,它首先將請求發送到Web伺服器,此時已經獲得了SessionID,接著瀏覽器會分別請求Frame中的其他頁面,這樣所有頁面的SessionID就是一樣的,就是FrameSet頁面的SessionID。然而如果你使用HTML頁面做FrameSet頁面,第一個請求將是HTML頁面,當該頁面從伺服器上返回時並沒有任何Session產生,接著瀏覽器會請求Frame裡面的頁面,這樣,這些頁面都會產生自己的SessionID,所以在這種情況下就可能出現這種問題。當你重新重新整理頁面時,SessionID就會一樣,並且是最後一個請求頁面的SessionID。

ASP.NET Session的七點認識


ASP.NET Session的七點認識之一
  對於實值型別的變數,Session中儲存的是實值型別的拷貝

 代碼如下 複製代碼
Session["__test0"] =1;
int i = (int)Session["__test0"]+1;
int j = (int)Session["__test0"];

  結果i=2,j=1

ASP.NET Session的七點認識之二
  對於引用類新的變數,Session中儲存的是引用

 代碼如下 複製代碼

CDACommon cda =new CDACommon();
Session["__test"] = cda.GetDataSet("select top 1 * from tb_customer");
DataSet ds = (DataSet)Session["__test"];
DataSet ds2 = (DataSet)Session["__test"];
ds.Tables[0].Rows[0][0]="9999";
  結果ds.Tables[0].Rows[0][0]=="9999" ds2.Tables[0].Rows[0][0]=="9999";

ASP.NET Session的七點認識之三
  Session周期

  新的瀏覽器視窗啟動後,開始一個新的Session,觸發Global的Session_Start的調用,從第一個瀏覽器視窗開啟的瀏覽器視窗不啟動新的Session。Session到期後,執行頁面的提交也會觸發Session_Start,等於是新的一個Session。

ASP.NET Session的七點認識之四
  調用Session

  對於Web Service,每個方法的調用都會啟動一個Session,可以用下面的方法來使多個調用在同一個Session裡

CWSSyscfg cwsCfg = new CWSSyscfg();
cwsCfg.CookieContainer = new System.Net.CookieContainer();
  CWSSyscfg是一個Web Service類,Web Service的給代理類設定CookieContainer屬性,只要多個代理的CookieContainer屬性是相同的值,則對這些Web Service的調用在同一個Session。可以用單例模式來實現。

ASP.NET Session的七點認識之五
  Session資料有效期間

  只要頁面有提交活動,則Session的所有項都會保持,頁面在20分鐘(預設配置)內沒有任何提交活動時Session會失效。Session記憶體儲的多個資料項目是整體失效的。

ASP.NET Session的七點認識之六
  Session的儲存

  在Session中如果儲存的是非序列化的類比如DataView,在用SQLServer儲存Session的模式下,無法使用。查看一個類是否是序列化的方法是,需看是否用[Serializable]來標記了該類。

ASP.NET Session的七點認識之七
  關於Sessuon的清除

  如果我在Session中儲存一個比較大的DataSet,這樣aspnet_wp.exe佔有的記憶體會很大,假如我退出了使用這個DataSet 的頁面,我想釋放該Session,我用Session.Clear() 或者DataSet.Clear()都不能使記憶體的佔用降下來,即使Session過了期限

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.