ASP.net session 使用总结(2)
Session又稱為會話狀態(tài),是Web系統(tǒng)中最常用的狀態(tài),用于維護和當前瀏覽器實例相關(guān)的一些信息。舉個例子來說,我們可以把已登錄用戶的用戶名放在Session中,這樣就能通過判斷Session中的某個Key來判斷用戶是否登錄,如果登錄的話用戶名又是多少。
我們知 道,Session對于每一個客戶端(或者說瀏覽器實例)是“人手一份”,用戶首次與Web服務(wù)器建立連接的時候,服務(wù)器會給用戶分發(fā)一個 SessionID作為標識。SessionID是一個由24個字符組成的隨機字符串。用戶每次提交頁面,瀏覽器都會把這個SessionID包含在 HTTP頭中提交給Web服務(wù)器,這樣Web服務(wù)器就能區(qū)分當前請求頁面的是哪一個客戶端。那么,ASP.NET 2.0提供了哪些存儲SessionID的模式呢:
·????? Cookie(默認)。如果客戶端禁止了Cookie的使用,Session也將失效。
·????? URL。Cookie是否開啟不影響Session使用,缺點是不能再使用絕對鏈接了。
前面說了SessionID可以存儲在客戶端的Cookie或者URL中,那么Session真正的內(nèi)容存儲在哪里呢?ASP.NET 2.0對于Session內(nèi)容的存儲也提供了多種模式。
·????? InProc(默認)。Session存儲在IIS進程中(Web服務(wù)器內(nèi)存)。
·????? StateServer。Session存儲在獨立的Windows服務(wù)進程中(可以不是Web服務(wù)器)。
·????? SqlServer。Session存儲在SqlServer數(shù)據(jù)庫的表中(SqlServer服務(wù)器)。
雖然 InProc模式的Session直接存儲在Web服務(wù)器IIS進程中,速度比較快,但是每次重新啟動IIS都會導(dǎo)致Session丟失。利用后兩種模式,我們就完全可以把Session從Web服務(wù)器中獨立出來,從而減輕Web服務(wù)器的壓力,同時減少Session丟失的概率。
因此,SessionID存儲在客戶端(可以是Cookie或者URL),其他都存儲在服務(wù)端(可以是IIS進程、獨立的Windows服務(wù)進程或者SQL Server數(shù)據(jù)庫中)。
12.3.2 ?Session的使用
讓我們先來實踐一下如何使用Session,進而回答第二個問題:Session存儲的類型限制。Session不需要進行任何配置就可以使用(默認是InProc模式并且依賴Cookie)。首先,在頁面上建立兩個按鈕。
<asp:Button ID="btn_WriteSession" runat="server"Text="寫入Session" />
<asp:Button ID="btn_ReadSession" runat="server" Text="讀取Session" />
在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按鈕來實現(xiàn)從Session中讀取數(shù)據(jù)的代碼:
protected void btn_ReadSession_Click(object sender, EventArgs e)
{
??? if (Session["SimpleString"]==null)
??? {
??????? Response.Write("讀取簡單字符串失敗<br/>");
??? }
??? else
??? {
??????? string s=Session["SimpleString"].ToString();
??????? Response.Write(s + "<br/>");
??? }
??? if (Session["CustomClass"]==null)
??? {
??????? Response.Write("讀取簡單自定義類失敗<br/>");
??? }
??? else
??? {
??????? MyUser user=Session["CustomClass"] as MyUser;
??????? Response.Write(user.ToString()+"<br/>");
??? }
}
在每次讀取Session的值以前請務(wù)必先判斷Session是否為空,否則很有可能出現(xiàn)“未將對象引用設(shè)置到對象的實例”的異常。我們看到,從Session 中讀出的數(shù)據(jù)都是object類型的,我們需要進行類型轉(zhuǎn)化后才能使用。打開頁面,先單擊寫入Session按鈕,再單擊讀取Session按鈕,頁面輸出如??? 圖12-1所示。
12.3.3 ?把Session存儲在獨立的進程中
由此看來,Session能存儲任意對象,是這樣嗎?現(xiàn)在得出這個結(jié)論還太早了一點,因為我們并沒有實踐過StateServer和SqlServer模式的Session。要把Session存儲在Windows服務(wù)進程中需要進行以下幾個步驟。
n? 第1步是打開狀態(tài)服務(wù)。依次打開“控制面板”→“管理工具”→“服務(wù)”命令,找到ASP.NET狀態(tài)服務(wù)一項,右鍵單擊服務(wù)選擇啟動,如圖12-2所示。
圖12-2 ?啟動ASP.NET狀態(tài)服務(wù)
n? 如果你正式?jīng)Q定使用狀態(tài)服務(wù)存儲Session前,別忘記修改服務(wù)為自啟動(在操作系統(tǒng)重啟后服務(wù)能自己啟動)以免忘記啟動服務(wù)而造成網(wǎng)站Session不能使用,如圖12-3所示,雙擊服務(wù)把服務(wù)的啟動類型設(shè)置為自動。
圖12-3 ?修改服務(wù)啟動類型為自動
服務(wù)正常啟動后可以觀察任務(wù)管理器的進程頁,其中的aspnet_state.exe進程就是狀態(tài)服務(wù)進程,如圖12-4所示。
圖12-4 ?觀察任務(wù)管理器的進程頁
n? 第2步,在system.web節(jié)點中加入:
<sessionState mode="StateServer" stateConnectionString="tcpip=127.0.0.1:42424"
stateNetworkTimeout="20"></sessionState>
n? stateConnectionString表示狀態(tài)服務(wù)器的通信地址(IP:服務(wù)端口號)。由于我們現(xiàn)在在本機進行測試,這里設(shè)置成本機地址127.0.0.1。狀態(tài)服務(wù)默認的監(jiān)聽端口為42422。當然,您也可以通過修改注冊表來修改狀態(tài)服務(wù)的端口號。
n? 1.在運行中輸入regedit啟動注冊表編輯器。
n? 2.依次打開HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\aspnet_state\Parameters節(jié)點,雙擊Port選項,如圖12-5所示。
選擇基數(shù)為十進制,然后輸入一個端口號即可。stateNetworkTimeout屬性表示從狀態(tài)服務(wù)器請求Session數(shù)據(jù)最長的時間,默認為10秒,如果網(wǎng)絡(luò)連接不是很好,請把這個數(shù)字適當設(shè)置得大一點。
n? 第3步打開頁面,單擊“寫入Session”按鈕,系統(tǒng)會報錯,如圖12-6所示。
???
??????? 圖12-5 ?修改狀態(tài)服務(wù)端口號??????????? 圖12-6 ?向StateServer默認的Session中寫入自定義類出錯
提示已經(jīng)說得很清楚了,只有把對象標注為可序列化后才能在服務(wù)中進行存儲。什么是序列化呢?序列化是指將對象實例的狀態(tài)存儲到存儲媒體的過程。在此過程中,先將對象的公共字段和私有字段以及類的名稱轉(zhuǎn)換為字節(jié)流,然后再把字節(jié)流寫入數(shù)據(jù)流。在隨后對對象進行反序列化時,將創(chuàng)建出與原對象完全相同的副本。要使一個類可序列化,最簡單的方法是使用 Serializable 屬性對它進行標記。
[Serializable]
class MyUser
{
??? public string sUserName;
??? public int iAage;
??? public override string ToString()
??? {
??????? return string.Format("姓名:{0},年齡:{1}", sUserName, iAage);
??? }
}
n? 第4步現(xiàn)在重新打開頁面進行測試,得到的結(jié)果和使用InProc模式是一樣的。
12.3.4 ?把Session存儲在數(shù)據(jù)庫中
要把Session存儲在SqlServer中,基本上也是這么幾個步驟。
n? 1.在命令行窗口輸入cmd并在命令行中運行如下命令。
C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\aspnet_regsql.exe -S .\SqlExpress -E –ssadd
其中 C:\Windows用你自己Windows的目錄代替,v2.0.50727用你安裝的2.0框架的版本號代替。-S指定SqlServer服務(wù)器地址,-E表示采用信任連接,-ssadd表示為SqlServer服務(wù)器添加狀態(tài)服務(wù)的支持。操作結(jié)束后,你可以使用IDE的服務(wù)器資源管理器連接 SqlExpress數(shù)據(jù)庫,可以看到多了一個ASPState數(shù)據(jù)庫,但是奇怪的是數(shù)據(jù)庫中沒有任何表卻有很多存儲過程,如圖12-7所示。
其實,所有Session的數(shù)據(jù)都存放在了tempdb數(shù)據(jù)庫內(nèi),如圖12-8所示。
???????????
??? 圖12-7 ?使用服務(wù)器資源管理器瀏覽ASPState數(shù)據(jù)庫????? 圖12-8 ?存放Session數(shù)據(jù)的tempdb數(shù)據(jù)庫
其實,aspnet_regsql.exe有一個-sstype參數(shù)可以用來指定Session的內(nèi)容和操作的存儲過程存放的表。由于篇幅關(guān)系,在這里就不詳細介紹了,讀者可以使用aspnet_regsql.exe/?來瀏覽程序詳細的使用方式。
n? 2.打開Web.config文件,修改前面建立的sessionState節(jié)點。
<sessionState mode="SQLServer" sqlConnectionString="server=(local)\SQLEXPRESS;
Trusted_Connection=True" sqlCommandTimeout="60"></sessionState>
為sqlConnectionString 屬性指定以前一直用的連接字符串,唯一不同的是不需要再指定數(shù)據(jù)表的名字了。sqlCommandTimeout屬性表示允許執(zhí)行Sql命令最長的時間,默認為30秒,可以根據(jù)自己的需要適當調(diào)整這個數(shù)字。最后,重新打開頁面進行測試,得到的結(jié)果和使用InProc模式是一樣的(同樣你需要確保在自定義類前標注了[Serializable]),不過我們能感到速度有些慢了,畢竟數(shù)據(jù)是從數(shù)據(jù)庫中進行讀取或保存的,而且在使用前還需要經(jīng)過序列化和反序列化操作。
因此Session能存儲的類型為: 對于InProc模式是一切類型,而對于StateServer和SqlServer模式是一切可以序列化的類型。
12.3.5 ?Session的使用范圍與大小限制
那么, 會話狀態(tài)使用的范圍和大小限制又是怎么樣的呢?我們可以分析一下圖12-8,系統(tǒng)使用兩個表來存儲Session的狀態(tài)。其中有一個 ASPStateTempApplication表,用來存儲Session所在的應(yīng)用程序,一定程度上反映了Session是不能跨應(yīng)用程序的。舉例來說,我們在計算機上建立了兩個網(wǎng)站,同時都使用Session[“UserName”]來保存登錄的用戶名,一個網(wǎng)站的用戶登錄后,另一個網(wǎng)站直接訪問 Session[“UserName”]是取不到任何值的。那么,Session是否可以跨用戶呢?通過前面的分析我們知道,肯定是不行的, Session通過SessionID來區(qū)分用戶,一般來說SessionID是不可能出現(xiàn)重復(fù)的現(xiàn)象,也就是說Session一般是不會“串號”的。既然頁面每次提交的時候都會附加上當前用戶的SessionID,那么Session應(yīng)該是可以跨頁面的,也就是說一個網(wǎng)站中所有的頁面都使用同一份 Session。你可以自己來做個試驗,請讀者打開剛才那個頁面,然后按Ctrl+N組合鍵再打開第二個同樣的頁面,單擊第一個頁面中的“寫入 Session”按鈕,單擊第二個頁面中的“讀取Session”按鈕,可以發(fā)現(xiàn)Session的值被正確讀出了。第三個問題的答案有了。
·????? Session狀態(tài)使用的范圍:使用同一個客戶端(瀏覽器實例)訪問同一個應(yīng)用程序的所有頁面。
我們再來做一個試驗,看看Session的容量有多大,在測試以前請修改Web.config,把Session設(shè)置為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重復(fù)加入ArrayList中1000萬次。由于這些表幾乎每個表只有幾條記錄,這樣可以模擬大數(shù)據(jù)量的情況。啟動頁面,單擊“寫入Session”按鈕后可以發(fā)現(xiàn),Windows服務(wù)進程一下子占用了多達70MB的內(nèi)存,如圖12-9所示。
圖12-9 ?把大量數(shù)據(jù)存放到Session中
Session 對于網(wǎng)站和用戶是獨立的,試想一下,如果服務(wù)器上有兩個網(wǎng)站,每個網(wǎng)站的在線人數(shù)是100人,那么占用內(nèi)存就要14G。是不是很恐怖的數(shù)字?因此,雖然 Session的大小沒有限制,但是我們千萬不能濫用Session。筆者推薦你在Session中存儲少于100K的數(shù)據(jù)。
·????? 如果你使用InProc模式的Session,存儲過多的數(shù)據(jù)會導(dǎo)致IIS進程被回收,引發(fā)Session不斷丟失。
·????? 如果你使用StateServer存儲Session,那么數(shù)據(jù)在存入Session以前需要進行序列化,序列化會消耗大量的CPU資源。
·????? 如果你使用SqlServer模式的Session,數(shù)據(jù)不但要序列化而且還是存儲在磁盤上,更不適合存儲大量數(shù)據(jù)。
12.3.6 ?Session的生命周期
在了解了Session中存儲的數(shù)據(jù)無大小限制后,我們可能要更多地關(guān)心Session的生命周期了。我們已經(jīng)知道,Session是在用戶第一次訪問網(wǎng)站的時候創(chuàng)建的,那么Session是什么時候銷毀的呢?Session使用一種平滑超時的技術(shù)來控制何時銷毀Session。默認情況下,Session 的超時時間(Timeout)是20分鐘,用戶保持連續(xù)20分鐘不訪問網(wǎng)站,則Session被收回,如果在這20分鐘內(nèi)用戶又訪問了一次頁面,那么20 分鐘就重新計時了,也就是說,這個超時是連續(xù)不訪問的超時時間,而不是第一次訪問后20分鐘必過時。這個超時時間同樣也可以通過調(diào)整Web.config 文件進行修改:
<sessionState timeout="30"></sessionState>
當然你也可以在程序中進行設(shè)置:
Session.Timeout = "30";
一旦Session超時,Session中的數(shù)據(jù)將被回收,如果再使用Session系統(tǒng),將給你分配一個新的SessionID。本節(jié)一開始我們就介紹了可以在URL中存儲SessionID,現(xiàn)在請你配置Web.config文件,設(shè)置Session超時時間為1分鐘,SessionID在URl中存放。打開頁面后單擊“寫入Session”按鈕,過1分鐘再次單擊按鈕并觀察SessionID是否變化。
<sessionState timeout="1" cookieless="true"></sessionState>
如圖12-10所示,SessionID的確發(fā)生了變化。
圖12-10 ?超時后SessionID發(fā)生變化
不過,你可別太相信Session的Timeout屬性,如果你把它設(shè)置為24小時,則很難相信24小時之后用戶的Session還在。Session是否存在,不僅僅依賴于Timeout屬性,以下的情況都可能引起Session丟失(所謂丟失就是在超時以前原來的Session無效)。
·????? bin目錄中的文件被改寫。asp.net有一種機制,為了保證dll重新編譯之后,系統(tǒng)正常運行,它會重新啟動一次網(wǎng)站進程,這時就會導(dǎo)致 Session丟失,所以如果有access數(shù)據(jù)庫位于bin目錄,或者有其他文件被系統(tǒng)改寫,就會導(dǎo)致Session丟失。
·????? SessionID丟失或者無效。如果你在URL中存儲SessionID,但是使用了絕對地址重定向網(wǎng)站導(dǎo)致URL中的SessionID丟失,那么原來的Session將失效。如果你在Cookie中存儲SessionID,那么客戶端禁用Cookie或者Cookie達到了IE中Cookie數(shù)量的限制(每個域20個),那么Session將無效。
·????? 如果使用InProc的Session,那么IIS重啟將會丟失Session。同理,如果使用StateServer的Session,服務(wù)器重新啟動Session也會丟失。
一般來說,如果在IIS中存儲Session而且Session的Timeout設(shè)置得比較長,再加上Session中存儲大量的數(shù)據(jù),非常容易發(fā)生Session丟失的問題。
最后, Session的安全性怎么樣呢?我們知道,Session中只有SessionID是存儲在客戶端的,并且在頁面每次提交的過程中加入HTTP頭發(fā)送給服務(wù)器。SessionID只是一個識別符,沒有任何內(nèi)容,真正的內(nèi)容是存儲在服務(wù)器上的。總的來說安全性還是可以的,不過筆者建議你不要使用 cookieless和SqlServer模式的Session。把SessionID暴露在URL中,把內(nèi)容存儲在數(shù)據(jù)庫中可能會發(fā)生攻擊隱患。
12.3.7 ?遍歷與銷毀Session
Session雖然很方便,但是要用好Session還需要自己不斷實踐,根據(jù)自己網(wǎng)站的特點靈活使用各種模式的Session。關(guān)于使用程序訪問Session,筆者還想補充兩點。
·????? 如何遍歷當前的Session集合。
System.Collections.IEnumerator SessionEnum = Session.Keys.GetEnumerator();
while (SessionEnum.MoveNext())
{
??? Response.Write(Session[SessionEnum.Current.ToString()].ToString() + "<br/>");
}
對于我們這個例子,輸出和圖12-1一樣。如果你僅僅為了監(jiān)視Session,也可以通過trace來獲得詳細信息。在Web.config的system.Web節(jié)點中添加:
<trace enabled="true"? pageOutput="true"/>
打開頁面后單擊“寫入Session”按鈕,頁面顯示如圖12-11所示。
圖12-11 ?使用trace觀察會話狀態(tài)
·????? 如何立刻讓Session失效。比如用戶退出系統(tǒng)后,Session中保存的所有數(shù)據(jù)全部失效,可以使用以下代碼來讓Session失效。
Session.Abandon();
12.3.8? Session的常見問題與總結(jié)
Session的基本知識就介紹到這里,現(xiàn)在再回頭看第一節(jié)中的幾個問題,你是否都能回答了呢?為了強化大家的概念,筆者就三種模式的Session進行了一個比較(假設(shè)都使用Cookie來存儲SessionID)。
表12.1? 三種模式的Session比較
| ? | ? InProc | ? StateServer | ? SQLServer |
| ? 存儲物理位置 | ? IIS進程(內(nèi)存) | ? Windows服務(wù)進程(內(nèi)存) | ? SQLServer數(shù)據(jù)庫(磁盤) |
| ? 存儲類型限制 | ? 無限制 | ? 可以序列化的類型 | ? 可以序列化的類型 |
| ? 存儲大小限制 | ? 無限制 | ||
| ? 使用范圍 | ? 當前請求上下文,對于每個用戶獨立 | ||
| ? 生命周期 | ? 第一次訪問網(wǎng)站的時候創(chuàng)建Session超時后銷毀 | ||
| ? 優(yōu)點 | ? 性能比較高 | ? Session不依賴Web服務(wù)器,不容易丟失 | |
| ? 缺點 | ? 容易丟失 | ? 序列化與反序列化消耗CPU資源 | ? 序列化與反序列化消耗CPU資源,從磁盤讀取Session比較慢 |
| ? 使用原則 | ? 不要存放大量數(shù)據(jù) | ||
在使用Session的過程中你可能還會遇到很多奇怪的問題,結(jié)束本節(jié)之前筆者列出了幾條常見的FAQ,供大家參考:
·????? 為什么每次請求的SessionID都不相同?
n? 可能是沒有在Session里面保存任何信息引起的,即程序中任何地方都沒有使用Session。只有在Session中保存了內(nèi)容后,Session才會和瀏覽器進行關(guān)聯(lián),此時的SessionID將不會再變化。
·????? 為什么當我設(shè)置cookieless為true后,在重定向的時候會丟失Session?
n? 當使用cookieless時,你必須使用相對路徑替換程序中的絕對路徑,如果使用絕對路徑,ASP.NET將無法在URL中保存SessionID。
·????? 有辦法知道應(yīng)用程序的Session在運行時占用了多少內(nèi)存嗎?
n? 沒有辦法,你可以通過觀察IIS進程(InProc模式)或者aspnet_state進程(StateServer模式)大致估計。
·????? 有沒有可能知道整個網(wǎng)站使用Session的用戶列表?
n? 對于InProc模式和StateServer模式很難,對于SqlServer模式你可以查詢存儲Session的表進行嘗試。
·????? 當頁面中設(shè)了frameset,發(fā)現(xiàn)在每個frame中顯示頁面的SessionID在第一次請求時都不相同,為什么?
n? 原因是你的frameset是放在一個HTML頁面上而不是ASPX頁面。在一般情況下,如果frameset是aspx頁面,當你請求頁面時,它首先將請求發(fā)送到Web服務(wù)器,此時已經(jīng)獲得了SessionID,接著瀏覽器會分別請求Frame中的其他頁面,這樣所有頁面的SessionID就是一樣的,就是FrameSet頁面的SessionID。然而如果你使用HTML頁面做FrameSet頁面,第一個請求將是HTML頁面,當該頁面從服務(wù)器上返回時并沒有任何Session產(chǎn)生,接著瀏覽器會請求Frame里面的頁面,這樣,這些頁面都會產(chǎn)生自己的SessionID,所以在這種情況下就可能出現(xiàn)這種問題。當你重新刷新頁面時,SessionID就會一樣,并且是最后一個請求頁面的SessionID。
轉(zhuǎn)載于:https://www.cnblogs.com/ivangao/archive/2009/07/01/1514818.html
總結(jié)
以上是生活随笔為你收集整理的ASP.net session 使用总结(2)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 求相亲相爱一家人歌词!
- 下一篇: 国外优秀开源PHP建站程序一览