zoukankan      html  css  js  c++  java
  • ASP.NET会话(Session)保存模式

    ASP.NET会话(Session)保存模式 

    今日抽空就说一下 Session .Net v1.0/v1.1 中的存储模式。大家可在 MSDN 2003 中搜索一下<sessionState>即可看到关于 Web.config 中的<sessionState>节点元素的描述,共有 OffInProcStateServerSQLServer 四种模式。OffInProc 分别指“不启用”、“进程内保存(默认值)”,此两种模式没啥讲的,所谓 InProc 就是把 Session 保存在 aspnet_wp.exe (Windows 2000 解析 ASP.NET页面所用的进程) w3wp.exe (Win2003 的进程) 中,一旦进程被中止或被重置,Session 将丢失。

    一、        引发 Session 丢失的几种原因

    动过手写代码的人都知道,Session 丢失是比较常见的事。以下是本人这几年所遇到的,能够引发 Session 丢失的原因,不敢说是百分百,丢失概率还是特别高的。错…,简直可以说是“相…当…”高哇 ^_^"

    1、    存放 Session 的电脑重启(废话,若这样都不丢,你神仙啊)

    2、    InProc 模式:aspnet_wp.exe w3wp.exe 在“任务管理器”中或其它情况下导致其进程被终止运行。

    3、    InProc 模式:修改 .cs 文件后,编译了两次(只编译一次,有时不会丢失)

    4、    InProc 模式:修改了 Web.config

    5、    InProc 模式,Windows 2003 环境:应用程序池回收、停止后重启

    6、    InProc 模式:服务器上 bin 目录里的 .dll 文件被更新

    以上列举的都是 InProc 模式下,容易引发解析 ASP.NET 应用程序重置的原因。是不是觉得很窝火?之前我也有这种感觉,慢慢就习惯啦,再后来就干脆不用这种模式了。于是乎,就有了使用下列两种模式的尝试,现写出来与大家一起分享。

    二、        使用 StateServer 保存 Session

    StateServer 模式的实质是,把Session 存放在一个单独的进程里,此进程独立于 aspnet_wp.exe w3wp.exe 。启用此服务后,在“任务管理器”中可以看到一个名为 aspnet_state.exe 的进程,下面开始说明一下设置的具体步骤:

    1、    修改注册表(关键步骤,如下图)

    运行 regedit →打开注册表→找到HKEY_LOCAL_MACHINE/SYSTEM/CurrentControlSet/Services/aspnet_state/Parameters 节点→将AllowRemoteConnection 的键值设置成“1”(1 为允许,0 代表禁止)→设置Port (端口号)

    注意事项:

           a)、若ASP.NET State Service 正在运行,修改注册表内容后,则需要重新启动该服务

    b)、注意端口号的键值是以十六进制储存的,可以使用十进制进行修改,42424 是默认的端口

    c)AllowRemoteConnection 的键值设置成“1”后,意味着允许远程电脑的连接,也就是说只要知道你的服务端口,就可享用你的ASP.NET State Service,即把 Session 存放在你的电脑进程内,因此请大家慎用;键值为“0”时,仅有stateConnectionString为“tcpip=localhost: 42424”与“tcpip=127.0.0.1:42424”的情况,方可使用ASP.NET State Service

     

    2、    开启 ASP.NET State Service(如下图)

    右键点击“我的电脑”→管理→服务与应用程序→服务→双击“ASP.NET State Service”→启动(可设为“自动”)

    说明:只要安装了 .Net Framework v1.0/v1.1 ,都拥有此服务。

     

    3、    更改 Web.config

    打开 Web.config →找到<sessionState>节点内容

    <sessionState

                mode="InProc"

                stateConnectionString="tcpip=127.0.0.1:42424"

                sqlConnectionString="data source=127.0.0.1;Trusted_Connection=yes"

                cookieless="false"

                timeout="20" />

     

    →将其改为以下内容

    <sessionState mode="StateServer" stateConnectionString="tcpip=192.168.0.2:42424" timeout="20" />

    注意事项:

           a)、设成StateServer 后,必须要有对应的stateConnectionString

           b)、注意 IP 地址(可以是远程计算机 IP、计算机名称、域名)与端口号,端口号需与ASP.NET State Service 的服务端口一致

    三、        Session 放入 SQLServer 保存

    SQLServer 模式就是,把Session 存放在 SQL Server 数据库里(注意不是 Oracle ,动动脚趾都能猜到原因啦),下面开始说明一下设置的具体步骤:

    1、    启动相关的数据库服务(如图)

    运行SQL Server 服务管理器→启动 SQL Server (最好设为开机自动运行)→启动 SQL Server Agent 服务(最好设为开机自动运行)

    注意事项:

           a)、注意启动顺序,也可通过下列方式设置:右键点击“我的电脑”→管理→服务与应用程序→服务→找到“MSSQLSERVER”与“SQLSERVERAGENT”→启动并设置启动类型为“自动”

    b)SQL Server Agent在此处的作用是清除数据库中已过期的 Session

     

     

    2、    建立存放 Session DataBase

    运行“SQL 查询分析器”→使用“sa”或是拥有“master”的 db_owner 权限的用户登录数据库→打开查询文件 C:/WINNT/Microsoft.NET/Framework/v1.1.4322/InstallSqlState.sql (存放在 Windows 系统目录的 .Net 安装目录下可找到)→直接运行该 sql 脚本→刷新数据库即可看到名为 ASPState DataBase

     

     

    3、    建立连接数据库 ASPState 的用户,并为此用户授权(此步骤可跳过)

    进行此步的原因是:一是不想在 Web.config 中出现 sa 的密码;二是 tempdb 在数据库启动后仅保留 sa 一个帐号的使用权限,其余帐号的权限统统被清除,但保存 Session又需要用到此 DataBase

    A)、运行 SQL Server 的企业管理器→展开数据库的安全性→右击“登录”→新建“登录”→输入“名称”→选择“SQL Server 身份验证”→输入“密码”→指定“数据库”→点击“数据库访问”→勾选“ASPState”→选中“db_owner”角色→点击“确定”→再一次输入“密码”→点击“确定”后即可建立 ASPState 的用户(此处建立名为“SessionStateUser”,密码为“123456”的测试用户)

     

       

     

    B)、运行 SQL Server 的企业管理器→展开“管理”→展开“SQL Server 代理”→右击“作业”→点击“新建作业”→输入“名称”(此例为 GrantSessionUser )→点击标签“步骤”→新建→输入“步骤名”(此例为 Grant01)→选择数据库“tempdb”→编写 SQL 脚本“exec sp_adduser 'SessionStateUser', 'SessionUser' ,'db_owner' ”→确定→点击标签“调度”→新建→输入“名称”(此例为 Start01 )→选择类型“SQL Server 代理启动时自动启动”→确定→最后点击“确定”新增完毕

     

    C)、也可运行以下脚本一次性搞定以上 AB 两个步骤

    /******脚本开始******/

          --新建数据库帐号 SessionStateUser ,默认登录 ASPState

    EXEC sp_addlogin 'SessionStateUser', '123456', 'ASPState'

    use ASPState        --切换 DataBase

          -- SessionStateUser 授予 db_owner 的权限

    exec sp_adduser 'SessionStateUser', 'SessionUser' ,'db_owner'

    use master            --切换 DataBase

    BEGIN TRANSACTION  

          /******声明变量******/        

        DECLARE @JobID BINARY(16) 

        DECLARE @ReturnCode INT   

        SELECT @ReturnCode = 0    

        -- 若没有,则添加作业的分类

        IF (SELECT COUNT(*) FROM msdb.dbo.syscategories WHERE name = N'[Uncategorized (Local)]') < 1

            EXECUTE msdb.dbo.sp_add_category @name = N'[Uncategorized (Local)]'

        -- 新建作业

        EXECUTE @ReturnCode = msdb.dbo.sp_add_job  --调用存储过程 sp_add_job

                @job_id = @JobID OUTPUT,           --将返回的 JobID,赋值给变量

                @job_name = N'GrantSessionUser',   --作业名称

                @owner_login_name = NULL,           --默认为当前用户所有

                @description = null,

                @category_name = N'[Uncategorized (Local)]',        --作业分类归属

                @enabled = 1,                    --是否启用

                @notify_level_email = 0,

                @notify_level_page = 0,

                @notify_level_netsend = 0,

                @notify_level_eventlog = 0,

                @delete_level= 0

        IF (@@ERROR <> 0 OR @ReturnCode <> 0) GOTO QuitWithRollback --出错则回滚

       

       -- 新建步骤

        EXECUTE @ReturnCode = msdb.dbo.sp_add_jobstep --调用存储过程 sp_add_jobstep

                @job_id = @JobID,                   --传入刚刚新建的 JobID

                @step_id = 1,

                @step_name = N'Grant01',         --步骤名称

                @command = N'exec sp_adduser ''SessionStateUser'', ''SessionUser'' ,''db_owner''',

                 --需要执行的 SQL 脚本(注意用两个连续的单引号表示 SQL 中的单引号)

                @database_name = N'tempdb', --执行上述 SQL 所用的 DataBase

                @server = N'',

                @database_user_name = N'',

                @subsystem = N'TSQL',    --执行类型为“Transact-SQL 脚本”

                @cmdexec_success_code = 0,

                @flags = 0,

                @retry_attempts = 0,

                @retry_interval = 1,

                @output_file_name = N'',

                @on_success_step_id = 0,

               @on_success_action = 1,

                @on_fail_step_id = 0,

                @on_fail_action = 2

        IF (@@ERROR <> 0 OR @ReturnCode <> 0) GOTO QuitWithRollback

       

        -- 新建调度

        EXECUTE @ReturnCode = msdb.dbo.sp_add_jobschedule

                @job_id = @JobID,

                @name = N'Start01',   --调度名称

                @enabled = 1,

                @freq_type = 64               --64”表示当 SQLServerAgent 服务启动时运行

        IF (@@ERROR <> 0 OR @ReturnCode <> 0) GOTO QuitWithRollback

       

        -- 将新建的作业添加到本地数据库

        EXECUTE @ReturnCode = msdb.dbo.sp_add_jobserver @job_id = @JobID, @server_name = N'(local)'

        IF (@@ERROR <> 0 OR @ReturnCode <> 0) GOTO QuitWithRollback

       

        COMMIT TRANSACTION         

        GOTO   EndSave             

    QuitWithRollback:

        IF (@@TRANCOUNT > 0) ROLLBACK TRANSACTION

    EndSave:

    /******脚本结束******/

    4、    设置 Web.config 内容

    打开 Web.config →找到<sessionState>节点内容→修改为以下内容即可:

    <sessionState mode="SQLServer" sqlConnectionString ="data source=192.168.0.2; user id=SessionStateUser; password=123456" timeout="20" />

    注意事项:

           a)sqlConnectionString中不能出现 initial catalog 选项

    b)SQL Server Agent在此处的作用是清除数据库中已过期的 Session

    c)、你若跳过了第三步,则user id 需要用sa进行登录

    d)、若sqlConnectionString为“data source=127.0.0.1;Trusted_Connection=yes”,则使用本地计算机ASPNETWindows 2000 系统帐户)或 Network ServiceWindows 2003 系统帐户)的身份登录数据库。要是数据库不允许上述用户登录,则报错;同样,即使上述帐户能成功登录,也要分配其 tempdb 的权限,理由是 Session 是保存在 tempdb 中的,若没有该 DataBase 的存取权限是行不滴。见下图:

     关于SESSION共享的问题个人总结。

    1.不管上面采用哪种模式要达到SESION共享必须是同一个站点或者主域名相同,要不绝对不可能实现SESSION。

    2.同时相关子模块项目是不允许在IIS设置虚拟目录,设置了虚拟目录也是不可能达到共享条件的。

    3.在本地开发时怎么达到SESSION共享的条件时布置呢?举例如下:

    3.1有个大项目A站点(假设他的域名为EXAMPLE.COM.CN)

    3.2其中有2个单独的项目为B,C(假设他的域名为B.EXAMPLE.COM.CN和C.EXAMPLE.COM.CN或是EXAMPLE.COM.CN/B和EXAMPLE.COM.CN/C),他们之间要共享SESSION。

    采取做法如下(详情可以参考自己参与的DP项目):

    3.3在同一个解决方案中在A中把增加对 B,C两个Web工程的引用,注意:是工程引用,不是其他引用(且这2个工程都在A功根目录地下,但B,C工程也在这解决方案中,打开VS A工程站点是看不到B,C文件夹的,同时出现与A同等级的B,C工程站点在解决方案中,这样开发就互不影响,从而达到分工程团队开发)。

    3.4.运行时在主站点直接运行看效果。那样在本地就可以看到SESSION的共享了。

    3.5.发布到IIS上要布置3个站点,好像同时要设置COOKIE的主域名DOMIAN为主站点的域名且有以下操作

    protected void Session_Start(object sender, EventArgs e)
            {
                Response.Cookies[
    "ASP.NET_SessionId"].Value = Session.SessionID.ToString();
                Response.Cookies[
    "ASP.NET_SessionId"].Domain =".local.com";
            }

    或写个httpModules层处理这个问题

    namespace Primepress.FontSearch.Services {

        using System;

        using System.Collections.Generic;

        using System.Web;

        using System.Web.SessionState;

        using System.Reflection;

        /// <summary>

        /// 二级域名会话共享

        /// </summary>

        public class SessionSharedHttpModule : IHttpModule     {

            string _rootDomain = null; //一级域名

            public void Dispose()         {

                //throw new NotImplementedException();

            }

            public void Init(HttpApplication context)         {

                _rootDomain = "localhost"; //一级域名赋值

                //去除一级域名以外信息(将www.dhlx.cn改为dhlx.cn,如果一级域名不是常量赋值的话)

                //_rootDomain = _rootDomain.Substring(_rootDomain.LastIndexOf('.', _rootDomain.LastIndexOf('.') - 1) + 1);

                //要实现会话共享还得修改OutOfProcSessionStateStore类下的一个私有的静态字段s_uribase

                //OutOfProcSessionStateStore的声明为:

                //internal sealed class OutOfProcSessionStateStore : SessionStateStoreProviderBase

                //s_uribase的声明为:

                //static string       s_uribase;

                //关于OutOfProcSessionStateStore类以及s_uribase字段的内容请查阅OutOfProcStateClientManager.cs文件

                //文件路径:Framework源代码\V2.0.5727\dd\ndp\fx\src\xsp\System\Web\State\OutOfProcStateClientManager.cs

                Type stateServerSessionProvider = typeof(HttpSessionState).Assembly.GetType("System.Web.SessionState.OutOfProcSessionStateStore");

                FieldInfo uriField = stateServerSessionProvider.GetField("s_uribase", BindingFlags.Static | BindingFlags.NonPublic);

                if (uriField == null)

                    throw new ArgumentException("UriField was not found");

                uriField.SetValue(null, _rootDomain);

                context.EndRequest += new EventHandler(context_EndRequest);

            }

            /// <summary>

            /// 从发送给客户端的Cookie集合中找出记录会话ID的Cookie

            /// 并修改它的Domain属性值为要共享的一级域名

            /// </summary>

            void context_EndRequest(object sender, System.EventArgs e)         {

                HttpApplication app = sender as HttpApplication;

                for (int i = app.Context.Response.Cookies.Count - 1; i >= 0; i--)             {

                    //ASP.NET_SessionId是默认的存储会话ID的key,如果修改了默认值这里要修改成一致的

                    if (app.Context.Response.Cookies[i].Name.Equals("ASP.NET_SessionId"))                 {

                        app.Context.Response.Cookies[i].Domain = _rootDomain;

                        return;

                    }

                }

            }

        }

    }

     

  • 相关阅读:
    How to alter department in PMS system
    Can't create new folder in windows7
    calculate fraction by oracle
    Long Wei information technology development Limited by Share Ltd interview summary.
    ORACLE BACKUP AND RECOVERY
    DESCRIBE:When you mouse click right-side is open an application and click left-side is attribution.
    ORACLE_TO_CHAR Function
    电脑BOIS设置
    JSP点击表头排序
    jsp+js实现可排序表格
  • 原文地址:https://www.cnblogs.com/scottpei/p/2913392.html
Copyright © 2011-2022 走看看