前段时间Gary问了我个面试题:“ASP.NET Session ID会不会不停的变化?”据说某人的答案是“IE浏览器访问时会变化”听了后立刻就觉得这说法应该不靠谱。
先看看Session的工作机制。Session本身分成两部分,一个是ID,一个是Data,ID部分是存储在客户浏览器请求的header中的,比如存放在Cookie或者是请求时的url中;而Data是存放在服务器上的某个dictionary里。当用户请求的时候,用户向服务器提交SessionID,服务器得到SessionID并以它为key在Dictionary里查找数据。因此,SessionID是用户和服务器状态数据的唯一联系,这个SessionID没必要频繁变化,也不能频繁变化。试想一下,假如SessionID是变化的,并且我们用的是Cookless的工作方式,那么SessionID就是在Url中体现的,也就意味着当服务器响应html给客户端的时候,就必须将所有链接中的SessionID提前变更,然后再响应数据。这时用户只开一个窗口刷新是没问题的,因为当前页的链接中总是包含正确的SessionID,但是假如用户右键链接打开新窗口,再回到原页面工作呢?这时就出问题了。因为用户打开新窗口后,向服务器发送了请求,而服务器在收到请求后,改变了SessionID,这时用户切回老窗口,老窗口链接中的SessionID还是没有变化,再点击,全是死链。因此,“SessionID是不断变化”的这种说法根本不可能成立。
为了证明我的观点的正确性,Gary到公司后特意做了实验,可结果却出乎意料。在一个ASP.NET页面上加入一个LinkButton或者Button,用Response.Write(Response.Session.SessionID)的方式显示SessionID,结果每次点击按钮SessionID都会改变。
这是为什么呢?要理解这个问题,首先要理解ASP.NET中Response的工作机制。ASP.NET中Response在默认情况下,响应的数据并不是在调用Response.Write时就传回客户端,而是等所有的渲染工作完成后,才一股脑儿把响应http报文header和data的内容返回给客户端。由于在用户没有提供SessionID的情况下,任何和Session有关的操作被触发时ASP.NET都会创建一个新的ID,因此Gary的实验中能得到SessionID数据,但可以注意到在Gary的实验中,并没有往Session中存放数据,只是查询了SessionID,所以,ASP.NET在开始响应时,发现并没有必要记录该用户的状态,所以释放了Session对象,结果就是在响应的报文中,用户浏览器并没有收SessionID的信息,再次请求时ASP.NET由于没有收到SessionID信息,再次创建了SessionID,周而复始,以至于看到“SessionID不断变化的假象”。
那么有没有办法在没有存放Session数据的情况下,就响应SessionID给客户端呢?当然也是有办法的。Response对象下有一个Buffered属性,在该属性被设置为false时,ASP.NET会立刻把现阶段收集到的报文header部打到客户端,之后的代码中就不能有涉及到操作头部的信息了,否则就会抛出Exception。因为ASP.NET在flush stream时,并不知道接下来的代码中会不会有涉及到Session的操作,所以即使之前没有存储Session数据,ASP.NET也会把SessionID立刻打回客户端,
这种立刻响应的连接方式叫做chunked data response,也是做comet数据推送的基石。


Comments:
You can leave a comment on this post if you login