System.Web.SessionState 注意事项 (Session可能不正常的状态)

分类:.Net知识问答| 发布:camnprbubuol| 查看: | 发表时间:2012/5/11

System.Web 命名空间提供使得可以进行浏览器与服务器通讯的类和接口。此命名空间包括提供有关当前 HTTP 请求的广泛信息的 HttpRequest 类、管理对客户端的 HTTP 输出的 HttpResponse 类以及提供对服务器端实用工具与进程的访问的 HttpServerUtility 类。System.Web 还包括用于 Cookie 操作、文件传输、异常信息和输出缓存控制的类。

System.Web.SessionState 命名空间提供可将特定于某个单个客户端的数据存储在服务器上的一个 Web 应用程序中的类和接口。会话状态数据用于向客户端提供与该应用程序保持持久连接的样式。状态信息可以存储在本地进程内存中,或者,对于网络场配置,也可以使用 ASP.NET 状态服务或 SQL Server 数据库将其存储在进程之外。会话状态可以与不支持 Cookie 的客户端一起使用。ASP.NET 可以配置为对客户端和服务器之间传输的 URL 字符串中的会话 ID 进行编码。

System.Web.SessionState.HttpSessionState 我们使用的Session实际上就是这个类的对象。Session在使用它后的第一次请求的时候被创建,这个可以通过Session.IsNewSession的值来进行判断。public sealed class HttpSessionState : ICollection, IEnumerable 从这个声明可以看出,HttpSessionState继承自ICollection接口,说明以后的Session["xxx"]都是Add进入到Session这个Collection中的。我们通过名字取得一个Session实际上使用了索引。

          属性:
IsCookieless: 获取一个值,该值指示会话 ID 是嵌入在 URL 中还是存储在 HTTP Cookie 中。如果会话嵌入在 URL 中,则为 true;否则,为 false。  
IsSynchronized:标识一个Session是不是同步的(线程安全),鉴于超级严重的多窗口操作Session互相覆盖问题,默认的Session.IsSynchronized的返回值是False. 
Timeout: 设置Session超时的时间。以“分钟”为单位。

System.Web.SessionState 还具有一个枚举型变量,System.Web.SessionState.SessionStateMode,用于表示Session存在的4种状态:
      InProc, 默认方式,与aspnet_wp进程一起执行。
      off, Session值被禁用。
      SQLServer ,会话状态正在使用进程外 SQL Server 存储状态信息。
      StateServer, 会话状态正在使用进程外 Windows NT Server 存储状态信息。

以下是从网络上摘抄过来的有关Session的一些问题,原始链接是(http://www.hackchina.cn/Article/base/daima1/200601/9172.html):
问:为什么当调用Session.Abandon时并没有激发Session_End方法?
答:首先Session_End方法只支持InProc(进程内的)类型的Session。其次要激发Session_End方法,必须存在Session(即系统中已经使用Session了),并且至少要完成一次请求(在这次请求中会调用该方法)。
问:为什么当我在InProc模式下使用Session会经常丢失?
答:该问题通常是由于应用程序被回收导致的,因为当使用进程内Session时,Session是保存在aspnet_wp进程中,当该进程被回收Session自然也就没有了,确定该进程是否被回收可以通过查看系统的事件查看器获得信息。
具体信息请参考:
Session variables are lost intermittently in ASP.NET applications
http://support.microsoft.com/default.aspx?scid=kb;en-us;Q316148
在1.0的时候也有一个bug会导致工作进程被回收并重启,该bug已经在1.1和sp2中修复。
关于该bug的详细信息请参考:
ASP.NET Worker Process (Aspnet_wp.exe) Is Recycled Unexpectedly. 
http://support.microsoft.com/default.aspx?scid=kb;en-us;Q321792
问:为什么当Session超时或者Abandoned后,新Session的ID和原来的相同?
答:因为SessionID是保存在客户端浏览器的实例里,当Session超时在服务器重新建立Session时,将使用浏览器传来的SessionID,所以当Session超时后,再重新建立后SessionID并不变。
问:什么类型的对象可以保存在Session里?
答:这依赖使用的Session的模式,当使用的是进程内(InProc)的Session那么可以轻松的保存任何对象。如果你使用了非InProc的模式,则只能保存可以序列化和反序列化的对象,如果此时保存的对象不支持序列化,则不能保存到这种模式(非InProc)的Session里。
问:为什么在Session_End中不能使用Response.Redirect和Server.Transfer方法跳转页面?
答:Session_End是一个在服务器内部激发的事件处理函数。它是基于一个服务器内部的计时器的,在激发该事件时服务器上并没有相关的HttpRequest对象,因此此时并不能使用Response.Redirect和Server.Transfer方法。
问:在Session_End中是否可以获得HttpContext对象?
答:不行,因为这个事件并没有和任何的请求(Request)相关联,没有基于请求的上下文
问:Session.Abandon和Session.Clear有何不同?
答:主要的不同之处在于当使用Session.Abandon时,会调用Session_End方法(InProc模式下)。当下一个请求到来时将激发Session_Start方法。而Session.Clear只是清除Session中的所有数据并不会中止该Session,因此也不会调用那些方法。
问:为了可以顺序访问Session的状态值,Session是否提供了锁定机制?
答:Session实现了Reader/Writer的锁机制:
当页面对Session具有可写功能(即页面有<%@ Page EnableSessionState="True" %>标记),此时直到请求完成该页面的Session持有一个写锁定。
当页面对Session具有只读功能(即页面有<%@ Page EnableSessionState="ReadOnly" %>标记),此时知道请求完成该页面的Session持有一个读锁定。
读锁定将阻塞一个写锁定;读锁定不会阻塞读锁定;写锁定将阻塞所有的读写锁定。这就是为什么两个框架中的同一个页面都去写同一个Session时,其中一个要等待另一个(稍快的那个)完成后,才开始写。
问:为什么当我设置cookieless为true是我在重定向的时候会丢失Session?
答:当使用cookieless时,你必须使用相对路径替换程序中的绝对路径,如果使用绝对路径ASP.NET将无法在URL中保存SessionID。
例如:将\myDir\mySubdir\default.aspx换成..\default.aspx即可。
问:我在我自己的类中该如何使用Session呢?
答:可以使用HttpContext.Current.Session方式使用,具体方法如下:
HttpContext.Current.Session["SessionKey"] = "SessionValue"; 
类似的你还可以使用这种方式使用Application对象。

--------------------------------------------------------------------------------------------------------------------------------------------------

其他资源
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnaspp/html/aspnetsessionstate.asp
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnbda/html/CachingArchch2.asp
http://www.411asp.net/home/tutorial/specific/web/sessions

2. FAQ问题列表
Q: session state在部分浏览器上工作,而在其他一些上不工作。为什么呢?
Q: 在InProc模式中,为什么我有时会丢失所有的session?
Q: session state在一些web服务器上工作,但是在其他服务器上不工作。
Q: 为什么session state不可用?
Q: 为什么session_end没有触发?
Q: 使用InProc模式时,为什么我的session变量频繁丢失?
Q: 在session超时或删除之后,为什么SessionID保持不变
Q: 为什么SessionID每一次请求都会改变
Q: Session.Abandon()和Session.Clear()有什么区别
Q: session的Timeout属性是一个滑动超时值吗?
Q: 我可以在ASP.NET和ASP之间共享session吗?
Q: 我可以在web应用程序(例如虚拟目录或者IIS的应用程序)间共享session state吗?
Q: 在session state中可以存储哪些类型的对象?
Q: 为什么我的请求在切换到SQLServer模式之后挂住了?
Q: 为什么Response.Redirect和Server.Transfer在Session_End中不工作?
Q: 在Session_End中,我可以获得一个有效的HttpSessionState对象和HttpContext对象吗?
Q: 在web service中如何使用session?
Q:我正在写一个HttpHandler,为什么session stae不工作?
Q: 我正在使用web farm,并且每当我重定向到其他服务器时,session state就会丢失?
Q: 如果使用cookieless,我该如何从一个HTTP页面重定向到一个HTTPS页面?
Q: session state有没有一个锁机制来安排对session的访问顺序?
Q: 我该如何检测一个session过期,然后重定向到另一个页面
Q: 在Session_End中,我尝试使用SQL做一些清理工作,但是失败了,请问为什么?
Q: 我使用的是SQLServer模式,为什么我的session不会过期
Q: 我有一个以htm为扩展名的frameset页面,并且我发觉其中包含的每个帧在第一次请求时都有一个不同的SessionID,这是为什么?
Q: 我将EnableSessionState设置为ReadOnly,但是在InProc模式下,我仍然可以修改session,为什么?
Q: 我将cookieless设置为true,在Redirect之后session变量丢失了,为什么?
Q: 将cookieless设置为true有哪些缺点
Q: 在InProc模式下,我用编程方式改变了session的超时时间,它触发了Session_End,为什么?
Q: 在SQLServer模式下,我可以把session state保存在除tempdb之外的数据库中吗?
Q: 如何防止将未加密的字符串放在我的连接字符串汇总?
Q: 在使用SQLServer模式时,我需要怎样的SQL权限?
Q: 我可以自己写定制的session state模式吗?
Q: 在SQLServer或StateServer模式下,序列化和反序列化如何工作?
Q: 我该如何让我的state server更安全?
Q: 我能否可以使用非global.asax中的处理程序来订阅SessionStateModule.End事件?
Q: 不同的应用程序可以把他们的session state保存在同一个SQL Server上的不同数据库中吗?


Q: session state在部分浏览器上工作,而在其他一些上不工作。为什么呢? 
A: 估计你没有使用cookieless,你必须保证你的浏览器支持cookie。请参考这份KB:http://support.microsoft.com/default.aspx?scid=kb;EN-US;q316112

Q: 在InProc模式中,为什么我有时会丢失所有的session? 
A: 请见理解session state模式的健壮性部分

Q: session state在一些web服务器上工作,但是在其他服务器上不工作。
A: 可能是机器名的问题,见http://support.microsoft.com/default.aspx?scid=kb;EN-US;q316112

Q: 为什么session state不可用?
A: 
- 首先,检查web.config、machine.config和Page标签来确认你启用了session state
参考资料:
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpguide/html/cpconsessionstate.asp
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpgenref/html/cpconpage.asp 
- 请注意session state并非在任何地方、任何时间都可以使用,它仅在HttpApplication.AcquireRequestState事件之后可用。例如,在 global.asax中的Application_OnAuthenticateRequest处理程序中,session state不可用
- 请确认System.Web.SessionState.SessionStateModule已包含在配置文件的< httpModules>节中。一个常见的例子是,出于性能考虑,SharePoint应用程序会把这个模块从web.config文件中移除,因此导致session不可用

Q: 为什么session_end没有触发?
A: 这是最常见的问题之一
1. 请记住session_end仅在InProc模式中可用
2. 关闭浏览器,session_end是不会触发的。HTTP是一种无状态协议,服务器没有办法知道你的浏览器是否已经关闭。
3. 当有n分钟(n=timeout值)的无操作或调用Session.Abandon时,Session_End才会触发
4. 对于情况1而言,Session_End将由一个后台线程触发,这表示:
a. Session_End中的代码使用工作者进程账号运行,如果你访问如数据库这样的资源时,可能会有权限问题。
b. 如果在Session_End中发生错误,程序不会通知发生了什么
5. 对于情况2而言,为了让Session_End触发,session state必须先存在。这意味着你必须在session state中存储一些数据,并且已经完成了至少一个请求
6. 还是对于情况2而言,Session_End仅在被丢弃的session被找到的时候才会触发。这样的话,如果你在同一个请求中创建并丢弃一个 session,由于session没有被保存,因此也不会被找到,Session_End将不会被调用。这是v1.0和v1.1中的bug。

Q: 使用InProc模式时,为什么我的session变量频繁丢失?
A: 有可能是应用程序资源回收引起的,见http://support.microsoft.com/default.aspx?scid=kb;en-us;Q316148 
在v1.0中有一个bug可能会导致工作者进程重启动。在v1.1和v 1.0sp2中已经修复。见http://support.microsoft.com/default.aspx?scid=kb;EN-US;321792

关于应用程序资源回收的详细信息,请见我的另一篇:FAQhttp://www.asp.net/Forums/ShowPost.aspx?tabindex=1&PostID=232621

Q: 在session超时或删除之后,为什么SessionID保持不变
A: 尽管在超时周期之后session state过期,sessionID将一直保持到浏览器session过期为止,也就是说,一个相同的sessionID可以有多次session超时,但是始终对应着一个相同的浏览器实例。

Q: 为什么SessionID每一次请求都会改变
A: 如果你的应用程序从未在session state中存储过数据。在这种情况下,那么每次请求都会创建一个新的session state(ID也是新的),但是不会被存储,因为里面什么数据都没有。

尽管如此,有两种例外可能产生相同的Session ID
- 如果用户使用相同的浏览器实例来请求另一个使用session state的页面,那么你每次获得的Session ID是相同的。详见“在session超时或删除之后,为什么SessionID保持不变?”
- 如果使用了Session_OnStart事件,即使session为空,asp.net也会保存session state。

Q: Session.Abandon()和Session.Clear()有什么区别
A: 主要的区别在于,如果你调用Session.Abandon(), Session_End将被触发(仅在InProcxi下适用),在下一个请求中,Session_Start触发。而Session.Clear()仅仅是清除数据,但没有删除session。

Q: session的Timeout属性是一个滑动超时值吗?
A: Session的Timeout是一个滑动过期时间,意思是一旦你的页面访问session state,过期时间就会向挪。注意,只要页面没有被禁用,在请求时页面就会自动访问session

Q: 我可以在ASP.NET和ASP之间共享session吗?
A:不可以。但是有一篇文章讲到了如何来绕过这个问题:http://www.msdn.microsoft.com/library/default.asp?url=/library/en-us/dnaspp/html/ConvertToASPNET.asp

当然也有一些第三方解决方案。

Q: 我可以在web应用程序(例如虚拟目录或者IIS的应用程序)间共享session state吗?
A:不能。

Q: 在session state中可以存储哪些类型的对象?
A:这是由你使用的模式决定的
- 如果你使用的是InProc模式,存储在session state中的对象是活对象,那么你就可以存储你创建的任何对象
- 如果你使用的是SQLServer或State Server模式,当处理一个请求时,session state中的对象对象将被序列化和反序列化,所以请确认你的对象都是可序列化的,而它们的类都作了可序列化标记。如果没有,session state将不会成功存储。在v1.0中,有一个bug,当这个问题发生时,如果使用SQLServer模式,请求可能在不知情的情况下被挂起。挂起的问题在v1.1和v1.0 sp3中已经修复。KB324479的QFE fix也包含了对这一问题的修复。

更多信息请见:http://support.microsoft.com/directory/article.asp?ID=KB;EN-US;q312112

Q: 为什么我的请求在切换到SQLServer模式之后挂住了?
A:请看问题“在session state中可以存储哪些类型的对象?”的答案

Q: 为什么Response.Redirect和Server.Transfer在Session_End中不工作?
A:Session_End是在服务器内部触发的,它基于一个内部的计时器。因此,在事件触发时,与任何HttpRequest对象无关。这也是为什么Response.Redirect 和Server.Transfer不工作的原因。

Q: 在Session_End中,我可以获得一个有效的HttpSessionState对象和HttpContext对象吗?
A:  你可以获得httpSessionState对象,你可以使用'Session'来访问该对象。但是你无法访问HttpContext,因为这个事件和请求没有任何关系。

Q: 在web service中如何使用session?
A: 需要在调用方使用一些技巧,你必须保存web服务使用的cookie。请见关于HttpWebClientProtocol.CookieContainer的MSDN文档。

尽管如此,如果你是通过代理对象从你的页面调用web服务,由于架构限制,web服务和你的页面无法共享session state。


如果你通过redirect调用web服务,这是可以完成的


Q:我正在写一个HttpHandler,为什么session stae不工作?

A: 你的HttpHandler接口必须实现标记接口IRequiresSessionState或IReadOnlySessionState,方能使用session state。


Q: 我正在使用web farm,并且每当我重定向到其他服务器时,session state就会丢失?
A: 为了在web farm中的不同服务器之间维护session state,IIS Metabase中的网站应用程序路径(例如 \LM\W3SVC\2)应该在所有的web服务器上保持一致(大小写敏感)。详见KB325056


Q: 如果使用cookieless,我该如何从一个HTTP页面重定向到一个HTTPS页面?
A: 尝试使用下面的代码:
String originalUrl = "/fxtest3/sub/foo2.aspx";
String modifiedUrl = "https://localhost" + Response.ApplyAppPathModifier(originalUrl);
Response.Redirect(modifiedUrl);


Q: session state有没有一个锁机制来安排对session的访问顺序?
A:session state实现了读写锁定机制:
- 对session state有写权限(如<%@ Page EnableSessionState="True" %> )的页面或帧将获得这个session的写锁,直到请求结束。
- 对session state有读权限(如<%@ Page EnableSessionState="ReadOnly" %> )的页面或帧将获得这个session的读锁,直到请求结束。 
- 读锁会阻塞写锁;读锁不会阻塞读锁;写锁会阻塞所有的读锁和写锁
- 这也是为什么当两个帧同时拥有session的访问权限时,一个帧必须等待另一帧先完成


Q: 我该如何检测一个session过期,然后重定向到另一个页面
A: 这是经常要遇到的问题,但不幸的是没有很简单的方法来完成它。我们将期待在一个主要版本中实现它。同时,如果你使用cookie,你可以在cookie中存储一个标志,这样你就可以区分新浏览器+新session及旧浏览器+过期session,下面的代码在session过期时会重定向到一个过期页面。
void Session_OnStart(Object sender, EventArgs e) { 
    HttpContext context = HttpContext.Current;
    HttpCookieCollection cookies = context.Request.Cookies;

    if (cookies["starttime"] == null) { 
        HttpCookie cookie = new HttpCookie("starttime", DateTime.Now.ToString()); 
        cookie.Path = "/"; 
        context.Response.Cookies.Add(cookie); 
    } 
    else { 
        context.Response.Redirect("expired.aspx"); 
    } 
}

Q: 在Session_End中,我尝试使用SQL做一些清理工作,但是失败了,请问为什么?
A: 首先,session_End仅在InProc模式下支持。
第二,Session_End是用运行工作者进程(aspnet_wp.exe)的帐号运行的,这个账号可以在machine.config中指定。因此,在你的Session_End中,如果使用integrity security连接SQL,它将使用工作者进程账号身份连接,这可能会引起登录失败,这要看你的SQL安全设置了。


Q: 我使用的是SQLServer模式,为什么我的session不会过期
A: 在SQLServer模式下,session过期是由SQL Agent使用一个注册任务完成的,请确认你的SQL Agent是否已经运行。

Q: 我有一个以htm为扩展名的frameset页面,并且我发觉其中包含的每个帧在第一次请求时都有一个不同的SessionID,这是为什么?
A: 原因是你的frameset页面是一个htm文件而不是一个aspx页面

在通常情况下,如果一个frameset页为一个aspx文件,当你请求该页面时,会首先发请求给web服务器,你会收到一个asp.net session cookie(其中保存着session id),然后浏览器会为frame发送一个单独的请求,而每个请求将拥有相同的session id。

然而,因为你的页面是一个htm文件,第一个请求就不会获得任何session cookie,因为页面是由asp处理的而非asp.net,然后浏览器会为每个帧发送单独的请求。但是,这次每个单独的请求将不会持有任何 session id,这样的话每个帧将创建自己的session。这也是为什么你在每个帧中看到的session id都不同。最后一个请求将赢得胜利,因为它将覆盖前两个请求写入的cookie。如果你刷新一次,你将看到它们拥有了相同的session id。

这个行为是设计所决定的,简单的解决方法就是将frameset页面改称aspx
 
Q: 我将EnableSessionState设置为ReadOnly,但是在InProc模式下,我仍然可以修改session,为什么?
A: 尽管那些EnableSessionState被设置为ReadOnly,但是在InProc模式中,用户仍然可以修改session。唯一的区别在于session在请求中不会被锁住,这一限制是设计所决定的。对于这一点没有在msdn中提到我表示抱歉。

Q: 我将cookieless设置为true,在Redirect之后session变量丢失了,为什么?
A: 如果你使用的是cookieless,你必须使用相对路径(如..\hello.aspx),而不是绝对路径(如\foo\bar\hello.aspx)。如果你使用的是绝对路径,ASP.NET不会将session id保存在url中。

Q: 将cookieless设置为true有哪些缺点
A: 设置cookieless=true表示一些潜在的规则,主要有:
1. 你不能在你的页面中使用绝对路径
2. 在http和https之间切换的话,你必须做一些额外的动作
3. 如果你的客户发送了一个链接到一个朋友,URL将包含session id,两个用户可以在同一时间使用相同的session id

Q: 在InProc模式下,我用编程方式改变了session的超时时间,它触发了Session_End,为什么?
A: 这是InProc的一个bug。如果你更改session的timeout值为另一个值,Session_End将被调用(但不会调用Session_Start)。我们期待在v2.0中能够修复这个错误。

Q: 在SQLServer模式下,我可以把session state保存在除tempdb之外的数据库中吗?
A: 是的。见KB311209。

Q: 如何防止将未加密的字符串放在我的连接字符串汇总?
A: 见sql trusted connection或者将连接字符串以加密数据形式保存在注册表中。详情请见,KB329250和KB329290。

Q: 在使用SQLServer模式时,我需要怎样的SQL权限?
A: 调用者需要对下面的存储过程拥有EXEC权限,
dbo.TempGetAppID
dbo.TempGetStateItem
dbo.TempGetStateItemExclusive
dbo.TempReleaseStateItemExclusive
dbo.TempInsertStateItemLong
dbo.TempInsertStateItemShort
dbo.TempUpdateStateItemLong
dbo.TempUpdateStateItemShort
dbo.TempUpdateStateItemShortNullLong
dbo.TempUpdateStateItemLongNullShort
dbo.TempRemoveStateItem
dbo.TempResetTimeout 
在v1.1中,你也需要对下面的存储过程拥有EXEC权限
dbo.TempGetStateItem2
dbo.TempGetStateItemExclusive2


请注意存储过程的拥有者必须对session state表(dbo.ASPStateTempSessions和 dbo.ASPStateTempApplications)拥有SELECT/INSERT/UPDATE/DELETE 权限。通常,拥有者是执行installsqlstate.sql(或者持久版本,见KB311209)的帐号来安装sql session state需要的表、存储过程、数据库


也请注意,如果你的session state表在tempdb中(默认情况下)如果你对SQL Server进行资源回收,所有在这张表上的权限设置将丢失。

Q: 我可以自己写定制的session state模式吗?
A:(待翻译)

Q: 在SQLServer或StateServer模式下,序列化和反序列化如何工作?
A: (待翻译)

Q: 我该如何让我的state server更安全?
A:如果state server和web server运行在一台机器上,通过设置HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\ aspnet_state\Param ters\AllowRemoteConnection的dword项为0,可以让state server仅在本地运行。这样就可以防止远程客户端连见到state server上。这一特性在v1.1中可用,在v1.0 sp3中也有。

state server必须受防火墙保护,以防止外部连接以保证真正安全。默认的端口是TCP 42424,你可以设置HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\ aspnet_state\Param ters\Port来改变它。如果是本地模式,除了127.0.0.1以外,屏蔽所有外来连接;如果是远程模式,显式的禁用所有的地址,除了对wev服务器的连接。


使用IPSec是另一种保护state server的方式。


Q: 我能否可以使用非global.asax中的处理程序来订阅SessionStateModule.End事件?
A: 答案是否定的。当SessionStateModule触发End事件时,只有定义在global.asax中的方法才会被触发

这是出于安全原因考虑的才对此进行限制。假设asp.net允许用户使用的其他的处理程序来处理End事件。在这种情况下,用户通常使用一个页面方法作为处理程序,当你在事件订阅时传入处理程序,处理程序将与你的程序运行在的HttpApplication实例关联。请注意, HttpApplication实例会被回收来处理其他请求。这样的话,当End事件触发时,asp.net将调用处理程序,而与之关联的 HttpApplication实例已经被另一个请求所使用,这样的情况将引发各种各样的问题。为了避免这种危险,在v1.0中决定进调用 Global.asax中定义的方法。希望你们都可以忍受这一限制。

Q: 不同的应用程序可以把他们的session state保存在同一个SQL Server上的不同数据库中吗? 
A: 答案是肯定的。详情请见:http://support.microsoft.com/default.aspx?scid=kb;EN-US;836680

来源:http://www.cnblogs.com/154691780/archive/2008/01/17/1043524.html
365据说看到好文章不转的人,服务器容易宕机
原创文章如转载,请注明:转载自郑州网建-前端开发 http://camnpr.com/
本文链接:http://camnpr.com/archives/569.html