
- 類(lèi)型:磁盤(pán)工具大。18.8M語(yǔ)言:中文 評(píng)分:5.0
- 標(biāo)簽:
我們還是簡(jiǎn)單的來(lái)復(fù)習(xí)一下Session吧:Session的數(shù)據(jù)時(shí)保存在服務(wù)器端,并且每個(gè)客戶端對(duì)應(yīng)不同Session。那么Session究竟是如何保存,如何區(qū)分客服端的了?我們還是沿用以前的方法來(lái)講吧,以一個(gè)demo開(kāi)始:
protected void Page_Load(object sender, EventArgs e)
{
string name = this.Request["Name"];
object sessionName = Session["Name"];
if (string.IsNullOrEmpty(name) && sessionName==null)
{
Response.Write("Please Enter your name!");
}
else
{
if (sessionName == null)
{
Session.Add("Name", name);
Response.Write("Set Session name and Session ID:"+Session.SessionID);
}
else
{
Response.Write("Get Session Name and Session ID:"+ Session.SessionID);
}
Response.Write(" Name:" + name);
}
}
假設(shè)我們的請(qǐng)求路徑為http://localhost:18385/WebForm1.aspx?name=majiang
第一次請(qǐng)求數(shù)據(jù)如下:
第二次請(qǐng)求數(shù)據(jù)了:
這里我們看見(jiàn)在第一次請(qǐng)求的返回頭里面有一個(gè)ASP.NET_SessionId,在第二次請(qǐng)求過(guò)程中這個(gè)請(qǐng)求頭里面也含有ASP.NET_SessionId,并且它的值剛好是Session.SessionID(我這里用的是asp.net4.5),我們可以猜測(cè)這個(gè)ASP.NET_SessionId就是用來(lái)區(qū)分不同的客戶端請(qǐng)求。那么這個(gè)值是什么時(shí)候生成的又是什么時(shí)候輸出的了?
首先我們需要知道我們用到的那個(gè)Session具體在什么地方定義的,其實(shí)它是定義于HttpContext的Session屬性中,我們一般的page也只是調(diào)用這個(gè)屬性而已。
public HttpSessionState Session
{
get
{
if (this.HasWebSocketRequestTransitionCompleted)
{
return null;
}
if (this._sessionStateModule != null)
{
lock (this)
{
if (this._sessionStateModule != null)
{
this._sessionStateModule.InitStateStoreItem(true);
this._sessionStateModule = null;
}
}
}
return (HttpSessionState) this.Items["AspSession"];
}
}
這里用到一個(gè)_sessionStateModule的變量,那么究竟在什么地方操作它們的了?在HttpContext中有兩個(gè)操作sessionStateModule方法如下:
internal void AddDelayedHttpSessionState(SessionStateModule module)
{
if (this._sessionStateModule != null)
{
throw new HttpException(SR.GetString("Cant_have_multiple_session_module"));
}
this._sessionStateModule = module;
}
internal void RemoveDelayedHttpSessionState()
{
this._sessionStateModule = null;
}
這兩個(gè)方法干什么的我就不說(shuō)了,它們是在什么地方調(diào)用的了。如果你開(kāi)發(fā)過(guò)asp.net,那么你應(yīng)該知道在SessionStateModule 類(lèi),它是一個(gè)IHttpModule的實(shí)現(xiàn)者專(zhuān)門(mén)用來(lái)管理Session的,在這個(gè)類(lèi)中有一個(gè)InitModuleFromConfig方法,該方法主要是在該類(lèi)的Init中調(diào)用,如喪我們來(lái)看看它的具體實(shí)現(xiàn)吧:
private void InitModuleFromConfig(HttpApplication app, SessionStateSection config) { if (config.Mode != SessionStateMode.Off) { app.AddOnAcquireRequestStateAsync(new BeginEventHandler(this.BeginAcquireState), new EndEventHandler(this.EndAcquireState)); app.ReleaseRequestState += new EventHandler(this.OnReleaseState); app.EndRequest += new EventHandler(this.OnEndRequest); this._partitionResolver = this.InitPartitionResolver(config); switch (config.Mode) { case SessionStateMode.InProc: if (HttpRuntime.UseIntegratedPipeline) { s_canSkipEndRequestCall = true; } this._store = new InProcSessionStateStore(); this._store.Initialize(null, null); break; case SessionStateMode.StateServer: if (HttpRuntime.UseIntegratedPipeline) { s_canSkipEndRequestCall = true; } this._store = new OutOfProcSessionStateStore(); ((OutOfProcSessionStateStore) this._store).Initialize(null, null, this._partitionResolver); break; case SessionStateMode.SQLServer: this._store = new SqlSessionStateStore(); ((SqlSessionStateStore) this._store).Initialize(null, null, this._partitionResolver); break; case SessionStateMode.Custom: this._store = this.InitCustomStore(config); break; } this._idManager = this.InitSessionIDManager(config); if (((config.Mode == SessionStateMode.InProc) || (config.Mode == SessionStateMode.StateServer)) && this._usingAspnetSessionIdManager) { this._ignoreImpersonation = true; } } }
這里主要是設(shè)置 this._store和 this._idManager 它們兩個(gè)變量,其中 this._store的設(shè)置根據(jù)Session的存儲(chǔ)類(lèi)型不同設(shè)置為不同的實(shí)例,這里的存儲(chǔ)方式有以下四種
public enum SessionStateMode
{
Off,
InProc,
StateServer,
SQLServer,
Custom
}
默認(rèn)的是SessionStateMode.InProc,所以默認(rèn)的this._store是一個(gè)InProcSessionStateStore實(shí)例,而this._idManager默認(rèn)是一個(gè)SessionIDManager實(shí)例。這個(gè)方法結(jié)束后我們的 this._store和 this._idManager這兩個(gè)變量就已經(jīng)有值了。在SessionStateModule類(lèi)中還有一個(gè)很重要的方法 BeginAcquireState:
private IAsyncResult BeginAcquireState(object source, EventArgs e, AsyncCallback cb, object extraData) { IAsyncResult result; bool sessionStateItem = true; bool flag3 = false; this._acquireCalled = true; this._releaseCalled = false; this.ResetPerRequestFields(); this._rqContext = ((HttpApplication) source).Context; this._rqAr = new HttpAsyncResult(cb, extraData); this.ChangeImpersonation(this._rqContext, false); try { if (EtwTrace.IsTraceEnabled(4, 8)) { EtwTrace.Trace(EtwTraceType.ETW_TYPE_SESSION_DATA_BEGIN, this._rqContext.WorkerRequest); } this._store.InitializeRequest(this._rqContext); bool requiresSessionState = this._rqContext.RequiresSessionState; if (this._idManager.InitializeRequest(this._rqContext, false, out this._rqSupportSessionIdReissue)) { this._rqAr.Complete(true, null, null); if (EtwTrace.IsTraceEnabled(4, 8)) { EtwTrace.Trace(EtwTraceType.ETW_TYPE_SESSION_DATA_END, this._rqContext.WorkerRequest); } return this._rqAr; } if ((s_allowInProcOptimization && !s_sessionEverSet) && (!requiresSessionState || !((SessionIDManager) this._idManager).UseCookieless(this._rqContext))) { flag3 = true; } else { this._rqId = this._idManager.GetSessionID(this._rqContext); } if (!requiresSessionState) { if (this._rqId != null) { this._store.ResetItemTimeout(this._rqContext, this._rqId); } this._rqAr.Complete(true, null, null); if (EtwTrace.IsTraceEnabled(4, 8)) { EtwTrace.Trace(EtwTraceType.ETW_TYPE_SESSION_DATA_END, this._rqContext.WorkerRequest); } return this._rqAr; } this._rqExecutionTimeout = this._rqContext.Timeout; if (this._rqExecutionTimeout == DEFAULT_DBG_EXECUTION_TIMEOUT) { this._rqExecutionTimeout = s_configExecutionTimeout; } this._rqReadonly = this._rqContext.ReadOnlySessionState; if (this._rqId != null) { sessionStateItem = this.GetSessionStateItem(); } else if (!flag3) { bool flag4 = this.CreateSessionId(); this._rqIdNew = true; if (flag4) { if (s_configRegenerateExpiredSessionId) { this.CreateUninitializedSessionState(); } this._rqAr.Complete(true, null, null); if (EtwTrace.IsTraceEnabled(4, 8)) { EtwTrace.Trace(EtwTraceType.ETW_TYPE_SESSION_DATA_END, this._rqContext.WorkerRequest); } return this._rqAr; } } if (sessionStateItem) { this.CompleteAcquireState(); this._rqAr.Complete(true, null, null); } result = this._rqAr; } finally { this.RestoreImpersonation(); } return result; }
在這個(gè)方法中有以下3句比較重要
this._rqId = this._idManager.GetSessionID(this._rqContext);
sessionStateItem = this.GetSessionStateItem();
this.CompleteAcquireState();
第一句獲取SessionID,第二句貨物SessionStateItem,第三句主要是調(diào)用一個(gè)CompleteAcquireState方法,而這個(gè)方法里面有一句 SessionStateUtility.AddDelayedHttpSessionStateToContext(this._rqContext, this);或則this.InitStateStoreItem(true); 這個(gè)方法主要對(duì)應(yīng)一句
SessionStateUtility.AddHttpSessionStateToContext(this._rqContext, this._rqSessionState);,在這個(gè)類(lèi)中還有一個(gè)方法OnReleaseState里面有這么一句
SessionStateUtility.RemoveHttpSessionStateFromContext(this._rqContext, delayed);
我們首先來(lái)可看看SessionStateUtility的AddHttpSessionStateToContext、RemoveHttpSessionStateFromContext方法的實(shí)現(xiàn)吧。
internal static void AddDelayedHttpSessionStateToContext(HttpContext context, SessionStateModule module){ context.AddDelayedHttpSessionState(module);}internal void AddDelayedHttpSessionState(SessionStateModule module){ if (this._sessionStateModule != null) { throw new HttpException(SR.GetString("Cant_have_multiple_session_module")); } this._sessionStateModule = module;}public static void AddHttpSessionStateToContext(HttpContext context, IHttpSessionState container) { HttpSessionState state = new HttpSessionState(container); try { context.Items.Add("AspSession", state); } catch (ArgumentException) { throw new HttpException(SR.GetString("Cant_have_multiple_session_module")); } } internal static void RemoveHttpSessionStateFromContext(HttpContext context, bool delayed) { if (delayed) { context.RemoveDelayedHttpSessionState(); } else { context.Items.Remove("AspSession"); } }
其中HttpContext的RemoveDelayedHttpSessionState就一句 this._sessionStateModule = null;我想對(duì)于SessionStateUtility里面的這幾個(gè)方法我就不多說(shuō)吧,很簡(jiǎn)單。
我們還是回頭看看前面那2句吧,
public string GetSessionID(HttpContext context){ string id = null; this.CheckInitializeRequestCalled(context); if (this.UseCookieless(context)) { return (string) context.Items["AspCookielessSession"]; } HttpCookie cookie = context.Request.Cookies[Config.CookieName]; if ((cookie != null) && (cookie.Value != null)) { id = this.Decode(cookie.Value); if ((id != null) && !this.ValidateInternal(id, false)) { id = null; } } return id;}
默認(rèn)情況下我們的cookie是可用的,這里的Config.CookieName實(shí)際上就是SessionStateSection的CookieName屬性
[ConfigurationProperty("cookieName", DefaultValue="ASP.NET_SessionId")]public string CookieName{ get { return (string) base[_propCookieName]; } set { base[_propCookieName] = value; }}
到這里大家應(yīng)該知道為什么Http請(qǐng)求和返回關(guān)于Session對(duì)應(yīng)Cookie的id是ASP.NET_SessionId了吧。不過(guò)大家要注意一點(diǎn)這里的SessionIDManager 在操作cookie做了一些數(shù)據(jù)驗(yàn)證處理,如果在特殊情況需要自定義驗(yàn)證規(guī)則我們可以自己來(lái)實(shí)現(xiàn)ISessionIDManager接口。這里我們可以看到第一次請(qǐng)求是沒(méi)有sessionid的,所以sessionStateItem = this.GetSessionStateItem();這句代碼不會(huì)執(zhí)行,sessionStateItem默認(rèn)為true,但是第二次請(qǐng)求時(shí)有sessionid這句代碼就會(huì)執(zhí)行。GetSessionStateItem()的實(shí)現(xiàn)這里我們就忽略了吧,這個(gè)方法設(shè)置一個(gè)SessionStateStoreData的實(shí)例 this._rqItem ,如果 this._rqItem為null則返回false。一般我們的Session都是可讀寫(xiě)的。GetSessionStateItem方法主要是調(diào)用 this._rqItem = this._store.GetItemExclusive(this._rqContext, this._rqId, out flag2, out span, out this._rqLockId, out this._rqActionFlags);
現(xiàn)在我們回到CompleteAcquireState方法中來(lái):
if (flag)
{
SessionStateUtility.AddDelayedHttpSessionStateToContext(this._rqContext, this);
this._rqSessionState = s_delayedSessionState;
}
else
{
this.InitStateStoreItem(true); //SessionStateUtility.AddHttpSessionStateToContext(this._rqContext, this._rqSessionState);
}
這里是flag默認(rèn)是false,里面具體判斷就不說(shuō),InitStateStoreItem方法主要代碼:
if (this._rqItem == null)
{
this._rqItem = this._store.CreateNewStoreData(this._rqContext, s_timeout);
}
this._rqSessionItems = this._rqItem.Items;
this._rqSessionState = new HttpSessionStateContainer(this, this._rqId, this._rqSessionItems, this._rqStaticObjects, this._rqItem.Timeout, this._rqIsNewSession, s_configCookieless, s_configMode, this._rqReadonly);
SessionStateUtility.AddHttpSessionStateToContext(this._rqContext, this._rqSessionState);
這里InProcSessionStateStore 的CreateNewStoreData方法實(shí)際就是調(diào)用SessionStateUtility.CreateLegitStoreData:
internal static SessionStateStoreData CreateLegitStoreData(HttpContext context, ISessionStateItemCollection sessionItems, HttpStaticObjectsCollection staticObjects, int timeout){ if (sessionItems == null) { sessionItems = new SessionStateItemCollection(); } if ((staticObjects == null) && (context != null)) { staticObjects = GetSessionStaticObjects(context); } return new SessionStateStoreData(sessionItems, staticObjects, timeout);}
其中SessionStateItemCollection的定義如下:
public sealed class SessionStateItemCollection : NameObjectCollectionBase, ISessionStateItemCollection, ICollection, IEnumerable
這里創(chuàng)建了一個(gè) HttpSessionStateContainer實(shí)例。我想大家到這里就應(yīng)該明白我們的Session實(shí)際上就是一個(gè)HttpSessionStateContainer實(shí)例。
好現(xiàn)在我來(lái)看 Session.SessionID這個(gè)是怎么實(shí)現(xiàn)的
public string SessionID
{
get
{
if (this._id == null)
{
this._id = this._stateModule.DelayedGetSessionId();
}
return this._id;
}
}
而SessionStateModule的DelayedGetSessionId方法實(shí)現(xiàn)如下:
internal string DelayedGetSessionId()
{
this.ChangeImpersonation(this._rqContext, false);
try
{
this._rqId = this._idManager.GetSessionID(this._rqContext);
if (this._rqId == null)
{
this.CreateSessionId();
}
}
finally
{
this.RestoreImpersonation();
}
return this._rqId;
}
這里的CreateSessionId具體是怎么創(chuàng)建我就不說(shuō)了吧,知道它是真正創(chuàng)建sessionid的就可以。而session的實(shí)際操作都是在ISessionStateItemCollection里面如HttpSessionStateContainer的Add方法:
public void Add(string name, object value)
{
this._sessionItems[name] = value;
}
而這里的_sessionItems實(shí)際上是this._rqItem.Items,本來(lái)想忽略_rqItem的創(chuàng)建,看來(lái)這個(gè)實(shí)例比較強(qiáng)啊。
this._rqItem = this._store.GetItemExclusive(this._rqContext, this._rqId, out flag2, out span, out this._rqLockId, out this._rqActionFlags);
if ((((this._rqItem == null) && !flag2) && (this._rqId != null)) && ((s_configCookieless != HttpCookieMode.UseUri) || !s_configRegenerateExpiredSessionId))
{
this.CreateUninitializedSessionState();
this._rqItem = this._store.GetItemExclusive(this._rqContext, this._rqId, out flag2, out span, out this._rqLockId, out this._rqActionFlags);
}
這里的CreateUninitializedSessionState方法實(shí)際就是調(diào)用this._store.CreateUninitializedItem(this._rqContext, this._rqId, s_timeout);
我們前面知道this._store這個(gè)可以取很多實(shí)例的,是SessionStateStoreProviderBase類(lèi)型,這里我們也已默認(rèn)的 InProcSessionStateStore(繼承SessionStateStoreProviderBase)來(lái)說(shuō)說(shuō)吧,相關(guān)方法:
private SessionStateStoreData DoGet(HttpContext context, string id, bool exclusive, out bool locked, out TimeSpan lockAge, out object lockId, out SessionStateActions actionFlags) { bool flag; string key = this.CreateSessionStateCacheKey(id); InProcSessionState state = (InProcSessionState) HttpRuntime.CacheInternal.Get(key); if (state == null) { return null; } ...... return SessionStateUtility.CreateLegitStoreData(context, state._sessionItems, state._staticObjects, state._timeout); } public override void CreateUninitializedItem(HttpContext context, string id, int timeout) { string key = this.CreateSessionStateCacheKey(id); SessionIDManager.CheckIdLength(id, true); InProcSessionState state = new InProcSessionState(null, null, timeout, false, DateTime.MinValue, NewLockCookie, 1); try { } finally { if (HttpRuntime.CacheInternal.UtcAdd(key, state, null, Cache.NoAbsoluteExpiration, new TimeSpan(0, timeout, 0), CacheItemPriority.NotRemovable, this._callback) == null) { PerfCounters.IncrementCounter(AppPerfCounter.SESSIONS_TOTAL); PerfCounters.IncrementCounter(AppPerfCounter.SESSIONS_ACTIVE); } } }
現(xiàn)在我們終于明白一個(gè)Sessionid對(duì)應(yīng)一個(gè)SessionStateStoreData,所以它能區(qū)分不同的用戶請(qǐng)求,這里的id就是我們前面的this._rqId了。
現(xiàn)在我們也總結(jié)一下吧,我們的HttpContext的Session屬性實(shí)際上是一個(gè)HttpSessionStateContainer實(shí)例(HttpSessionStateContainer繼承IHttpSessionState),而它數(shù)據(jù)成員都是保存在ISessionStateItemCollection實(shí)例中,每一次http請(qǐng)求我們都會(huì)去獲取它的Sessionid,第一次請(qǐng)求sessionid問(wèn)null,我們沒(méi)有對(duì)應(yīng)的SessionStateStoreData數(shù)據(jù),這時(shí)我們?cè)赟essionStateModule的 InitStateStoreItem方法調(diào)用SessionStateStoreProviderBase的CreateNewStoreData方法來(lái)創(chuàng)建一個(gè)SessionStateStoreData實(shí)例,其中該實(shí)例有一個(gè)成員變量類(lèi)型是ISessionStateItemCollection用來(lái)保存用戶session的數(shù)據(jù)。同一個(gè)用戶第二次請(qǐng)求我們能獲取到它的sessionid,默認(rèn)也能獲取到SessionStateStoreData實(shí)例(session過(guò)期則取不到)。一個(gè)用戶對(duì)應(yīng)一個(gè)SessionStateStoreData,每個(gè)SessionStateStoreData里面有一個(gè)ISessionStateItemCollection實(shí)例用來(lái)保存用戶數(shù)據(jù),至于sessionid也就是用戶身份的區(qū)別依賴(lài)于ISessionIDManager的實(shí)現(xiàn)。