asp.net下session的应用

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 )。首先,在页面上建立两个按钮。
<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 按钮来实现从 Session 中读取数据的代码:
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 的值以前请务必先判断 Session 是否为空,否则很有可能出现 未将对象引用设置到对象的实例 的异常。我们看到,从 Session 中读出的数据都是 object 类型的,我们需要进行类型转化后才能使用。打开页面,先单击写入 Session 按钮,再单击读取 Session 按钮,页面输出如     12-1 所示。
12.3.3  Session 存储在独立的进程中
由此看来, Session 能存储任意对象,是这样吗?现在得出这个结论还太早了一点,因为我们并没有实践过 StateServer SqlServer 模式的 Session 。要把 Session 存储在 Windows 服务进程中需要进行以下几个步骤。
1 步是打开状态服务。依次打开 控制面板 ”→“ 管理工具 ”→“ 服务 命令,找到 ASP.NET 状态服务一项,右键单击服务选择启动,如图 12-2 所示。
12-2   启动 ASP.NET 状态服务
如果你正式决定使用状态服务存储 Session 前,别忘记修改服务为自启动(在操作系统重启后服务能自己启动)以免忘记启动服务而造成网站 Session 不能使用,如图 12-3 所示,双击服务把服务的启动类型设置为自动。
12-3   修改服务启动类型为自动
服务正常启动后可以观察任务管理器的进程页,其中的 aspnet_state.exe 进程就是状态服务进程,如图 12-4 所示。
12-4   观察任务管理器的进程页
2 步,在 system.web 节点中加入:
<sessionState mode=”StateServer” stateConnectionString=”tcpip=127.0.0.1:42424”
stateNetworkTimeout=”20”></sessionState>
n  stateConnectionString 表示状态服务器的通信地址( IP :服务端口号)。由于我们现在在本机进行测试,这里设置成本机地址 127.0.0.1 。状态服务默认的监听端口为 42422 。当然,您也可以通过修改注册表来修改状态服务的端口号。
n  1 .在运行中输入 regedit 启动注册表编辑器。
n  2 .依次打开 HKEY_LOCAL_MACHINE/SYSTEM/CurrentControlSet/Services/aspnet_state/Parameters 节点,双击 Port 选项,如图 12-5 所示。
选择基数为十进制,然后输入一个端口号即可。 stateNetworkTimeout 属性表示从状态服务器请求 Session 数据最长的时间,默认为 10 秒,如果网络连接不是很好,请把这个数字适当设置得大一点。
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);
    }
}
4 步现在重新打开页面进行测试,得到的结果和使用 InProc 模式是一样的。
12.3.4  Session 存储在数据库中
要把 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 服务器地址, -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 节点。
<sessionState mode=”SQLServer” sqlConnectionString=”server=(local)/SQLEXPRESS;
Trusted_Connection=True” sqlCommandTimeout=”60”></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 文件进行修改:
<sessionState timeout=”30”></sessionState>
当然你也可以在程序中进行设置:
Session.Timeout = “30”;
一旦 Session 超时, Session 中的数据将被回收,如果再使用 Session 系统,将给你分配一个新的 SessionID 。本节一开始我们就介绍了可以在 URL 中存储 SessionID ,现在请你配置 Web.config 文件,设置 Session 超时时间为 1 分钟, SessionID URl 中存放。打开页面后单击 写入 Session” 按钮,过 1 分钟再次单击按钮并观察 SessionID 是否变化。
<sessionState timeout=”1” cookieless=”true”></sessionState>
如图 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() + “<br/>”);
}
对于我们这个例子,输出和图 12-1 一样。如果你仅仅为了监视 Session ,也可以通过 trace 来获得详细信息。在 Web.config system.Web 节点中添加:
<trace enabled=”true”  pageOutput=”true”/>
打开页面后单击 写入 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 都不相同?
可能是没有在 Session 里面保存任何信息引起的,即程序中任何地方都没有使用 Session 。只有在 Session 中保存了内容后, Session 才会和浏览器进行关联,此时的 SessionID 将不会再变化。
为什么当我设置 cookieless true 后,在重定向的时候会丢失 Session
当使用 cookieless 时,你必须使用相对路径替换程序中的绝对路径,如果使用绝对路径, ASP.NET 将无法在 URL 中保存 SessionID
有办法知道应用程序的 Session 在运行时占用了多少内存吗?
没有办法,你可以通过观察 IIS 进程( InProc 模式)或者 aspnet_state 进程( StateServer 模式)大致估计。
·      有没有可能知道整个网站使用 Session 的用户列表?
对于 InProc 模式和 StateServer 模式很难,对于 SqlServer 模式你可以查询存储 Session 的表进行尝试。
当页面中设了 frameset ,发现在每个 frame 中显示页面的 SessionID 在第一次请求时都不相同,为什么?
原因是你的 frameset 是放在一个 HTML 页面上而不是 ASPX 页面。在一般情况下,如果 frameset aspx 页面,当你请求页面时,它首先将请求发送到 Web 服务器,此时已经获得了 SessionID ,接着浏览器会分别请求 Frame 中的其他页面,这样所有页面的 SessionID 就是一样的,就是 FrameSet 页面的 SessionID 。然而如果你使用 HTML 页面做 FrameSet 页面,第一个请求将是 HTML 页面,当该页面从服务器上返回时并没有任何 Session 产生,接着浏览器会请求 Frame 里面的页面,这样,这些页面都会产生自己的 SessionID ,所以在这种情况下就可能出现这种问题。当你重新刷新页面时, SessionID 就会一样,并且是最后一个请求页面的 SessionID
 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值