C#自定义Session


相比较微软传统的Session有诸多限制,Session设置InProc就会在IIS进程中,收到IIS进程影响,Session之间不能交互。

自定义Session就可以让我们实现,Session之间的交互,现实点的功能是,消息推送,A要发送消息给B,B要在网页里显示有最新的消息,并显示内容,普通的方式,是用Jq不停的去查,但这样就有可能给数据库造成比较大的压力,但如果把最新消息存于Session中,这样就比较容易了,因为Session数据是在内存中的,内存读取速度快,还有就是取得比较方便,对CPU资源损耗也比较小,这样也能有更高的用户体验。

自定义的Session代码如下:

using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Security.Cryptography;
using System.Text;
using System.Threading;

namespace KKCMS.Frame.SessionCommon
{
    /// <summary>
    /// 表示Session创建的工厂
    /// </summary>
    public class SessionFactory
    {
        private const string COOKIE_CODE_FORMAT = "id={0}&timestap={1}";

        private const string IP_USER_AGENT = "ip={0}&userAgent={1}&cookieCode={2}";

        private const string X_TWO = "x2";
        class QueueCookie
        {
            public bool IsOpen { get; set; }

            public bool StackCheck { get; set; }

            public DateTime LastExpTime { get; set; }

            public QueueCookie()
            {
            }
        }

        /// <summary>
        /// 表示Session建立引发的事件
        /// </summary>
        public event EventHandler Session_Start;

        /// <summary>
        /// 表示Session结束引发的事件
        /// </summary>
        public event EventHandler Session_End;

        private long id = 0;

        private bool close = false;

        private QueueCookie idCookie = new QueueCookie();

        private QueueCookie qCookie = new QueueCookie();

        private ConcurrentDictionary<string, Session> sessionDic = new ConcurrentDictionary<string, Session>();

        private Queue<Session> expSession = new Queue<Session>();

        private ConcurrentDictionary<long, ConcurrentQueue<string>> expKeyDic = new ConcurrentDictionary<long, ConcurrentQueue<string>>();

        private Thread idThread = null;

        private Thread holdThead = null;

        public SessionFactory()
        {
            #region 维持Id计数的线程
            idThread = new Thread(IdCheckRun);
            idThread.IsBackground = true;
            idThread.SetApartmentState(ApartmentState.MTA);
            idThread.Start();
            #endregion
            #region 管理session的线程
            holdThead = new Thread(KeepSession);
            holdThead.IsBackground = true;
            holdThead.SetApartmentState(ApartmentState.MTA);
            holdThead.Start();
            #endregion
        }
        ~SessionFactory()
        {
            try
            {
                //线程释放
                close = true;
                idThread.Interrupt();
                holdThead.Interrupt();
            }
            catch (Exception)
            {

            }
        }

        [MTAThread()]
        private void IdCheckRun()
        {
            //id值每隔三秒重置一次,相当于Session里的LICD
            int sleeptime = 1000 * 3600 * 15;
            while (!close)
            {
                Thread.Sleep(sleeptime);
                lock (idCookie)
                {
                    idCookie.IsOpen = true;
                    id = 0;//重置id
                    idCookie.IsOpen = false;
                }
            }
        }

        /// <summary>
        /// SHA512加密
        /// </summary>
        /// <param name="code"></param>
        /// <returns></returns>
        private string ConvertToSHA512(string code)
        {
            SHA512 sha = new SHA512CryptoServiceProvider();
            byte[] data = sha.ComputeHash(Encoding.UTF8.GetBytes(code));
            StringBuilder sb = new StringBuilder();
            foreach (byte b in data)
            {
                sb.Append(b.ToString(X_TWO));
            }
            return sb.ToString();
        }

        /// <summary>
        /// SHA256加密
        /// </summary>
        /// <param name="code"></param>
        /// <returns></returns>
        private string ConvertToSHA256(string code)
        {
            SHA256 sha2 = new SHA256CryptoServiceProvider();
            byte[] data = sha2.ComputeHash(Encoding.UTF8.GetBytes(code));
            StringBuilder sb = new StringBuilder();
            foreach (byte b in data)
            {
                sb.Append(b.ToString(X_TWO));
            }
            return sb.ToString();
        }

       
        /// <summary>
        /// 管理Session的方法执行(线程)
        /// </summary>
        [MTAThread()]
        private void KeepSession()
        {
            while (!close)
            {
                //每隔一秒查一次
                Thread.Sleep(1000);
                lock(qCookie)
                {
                    if (qCookie.StackCheck && DateTime.Now.CompareTo(qCookie.LastExpTime) == 1)
                    {
                        //先判断是否要执行Session清理
                        DateTime currentTime = qCookie.LastExpTime;
                        bool isout= false;
                        while (expSession.Count > 0&&!isout)
                        {
                            Session session = expSession.Dequeue();
                            sessionDic.TryRemove(session.Id, out session);
                            if (Session_End != null)
                            {
                                Session_End(session, new EventArgs());
                            }
                            if (expSession.Count!=0&&
                                DateTime.Now.CompareTo(expSession.Peek().ExpTime)<1)
                            {
                                //判断此时队列头上的Session是否过期
                                isout = true;
                            }
                        }
                        if (expSession.Count != 0)
                        {
                            //更新下一个Session的过期时间
                            qCookie.LastExpTime = expSession.Peek().ExpTime;
                        }
                    }
                }
            }
        }



        /// <summary>
        /// 尝试通过传入的cookieCode,ip,UserAgent来获取session
        /// </summary>
        /// <param name="cookieCode">cookieCode</param>
        /// <param name="ipAddress">ip</param>
        /// <param name="userAgent">UserAgent</param>
        /// <param name="session">返回session</param>
        /// <returns></returns>
        public bool TryGetSession(string cookieCode, string ipAddress, string userAgent, out Session session)
        {
            //cookieCode是一部分
            //另一部分需要结合ipAdress,userAgent,cookieCode用SHA512加密得到
            string userCode = ConvertToSHA512(string.Format(IP_USER_AGENT, ipAddress, userAgent, cookieCode)).ToUpper();
            string sessionId = cookieCode + userCode;
            if (sessionDic.TryGetValue(sessionId, out session))
            {
                return true;
            }
            else
            {
                return false;
            }
        }

        /// <summary>
        /// 创建session
        /// </summary>
        /// <param name="ipAddress"></param>
        /// <param name="timestap"></param>
        /// <param name="expTime"></param>
        /// <param name="userAgent"></param>
        /// <param name="idCookies"></param>
        /// <returns></returns>
        public Session CreateSession(string ipAddress, DateTime timestap, int expTime, string userAgent, out string idCookies)
        {
            long time = (long)(new TimeSpan(timestap.Ticks).TotalMilliseconds);
            long mId = 0;
            //id加1
            lock (idCookie)
            {
                idCookie.IsOpen = true;
                id++;
                idCookie.IsOpen = false;
                mId = id;
            }
            //Session过期时间
            DateTime dexpTime = timestap.AddMilliseconds(expTime);
            #region 采用不对称验证方式
            //idCookies是存入浏览器的,验证时,将cookie里的值和IP加UserAgent值连接,才能找到对应的SessionId
            //这样可以防止伪造SessionId,因为传入的IP虽可以伪造但确定不了哪个IP是与cookie里的值对应的,UserAgent也是一样
            //另一方面采用这种方式,可以使得idCookies+userCode产生的结果重复性非常小
            idCookies = ConvertToSHA256(string.Format(COOKIE_CODE_FORMAT, mId, time)).ToUpper();
            //idCookies += time.ToString(X_TWO).ToUpper();
            string userCode = ConvertToSHA512(string.Format(IP_USER_AGENT, ipAddress, userAgent, idCookies)).ToUpper();
            #endregion
            string sessionId = idCookies + userCode;
            Session session = new Session(sessionId, dexpTime, this);
            if (!sessionDic.TryAdd(sessionId, session))
            {
                throw new Exception("创建会话时出现异常!");
            }
            else
            {
                //触发SessionStart事件
                if (Session_Start != null)
                {
                    Session_Start(session, new EventArgs());
                }
                lock(qCookie)
                {
                    //这是用来清理Session的,StackCheck是一个标志位,表示初始Session放入
                    qCookie.StackCheck = true;
                    qCookie.LastExpTime = session.ExpTime;
                    expSession.Enqueue(session);
                }
            }
            return session;
        }

        /// <summary>
        /// 结束当前session
        /// </summary>
        /// <param name="sessionId"></param>
        public void ClearSession(string sessionId)
        {
            Session session = null;
            sessionDic.TryRemove(sessionId, out session);
        }


    }

    /// <summary>
    /// 表示一个当前会话
    /// </summary>
    public sealed class Session : Dictionary<string, object>
    {
        /// <summary>
        /// 获取SessionId
        /// </summary>
        public string Id { get; private set; }

        /// <summary>
        /// 获取Session过期时间
        /// </summary>
        public DateTime ExpTime { get; private set; }

        private SessionFactory BaseFactory { get; set; }

        public Session(string sessionId, DateTime expTime, SessionFactory factory)
        {
            Id = sessionId;
            ExpTime = expTime;
            BaseFactory = factory;
        }

        /// <summary>
        /// 取消当前会话
        /// </summary>
        public void Cancel()
        {
            BaseFactory.ClearSession(Id);
        }
    }
}
View Code

使用之前,可以配上一个过滤器,我这里使用的就是MVC过滤器

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using System.IO;

namespace KKCMS
{
    public class CommonFilter:ActionFilterAttribute
    {
        private static readonly KKCMS.Frame.SessionCommon.SessionFactory Factory=new KKCMS.Frame.SessionCommon.SessionFactory();
        private const string REMOTE_ADDRESS = "REMOTE_ADDR";
        private const string SESSION_ID_COOKIE_NAME = "MY_SESSIONID";

        public override void OnActionExecuting(ActionExecutingContext filterContext)
        {
            var context = filterContext.HttpContext;
            var request = context.Request;
            var response = context.Response;
            string ip = request.ServerVariables[REMOTE_ADDRESS];
            int expTime = 7200000;//过期时间为两个小时
            DateTime timestap = context.Timestamp;
            string userAgent = request.UserAgent;
            KKCMS.Frame.SessionCommon.Session resultSession = null;//表示获取的Session
            string cookieCode = request.Cookies?[SESSION_ID_COOKIE_NAME]?.Value;//?是空传播符,C#6.0才有的,非6,0可以加上非空判断
            if (cookieCode == null || !Factory.TryGetSession(cookieCode, ip, userAgent, out resultSession))
            {
                resultSession =Factory.CreateSession(ip, timestap, expTime, userAgent, out cookieCode);
                response.SetCookie(new HttpCookie(SESSION_ID_COOKIE_NAME, cookieCode));//设置SessionId
            }
            base.OnActionExecuting(filterContext);
        }

    }
}

  


免责声明!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系本站邮箱yoyou2525@163.com删除。



 
粤ICP备18138465号  © 2018-2025 CODEPRJ.COM