今天面试,其中有一个题目是怎样统计一个网站的在线访问人数,由于平时都把时间花在ssh或者springMvc框架上,所以对于session和Cookie的使用记得不是很清楚,所以直接回答不会,回到宿舍从新整理了一下jsp的基础知识,其实实现也挺简单的。
首先一点,为什么要使用Session和Cookie,它们所起的作用是什么?平时我们都会说,cookie存放在客户端,一般存放在浏览器中用来保存用户的登录信息;而Session是保存在服务器上的,用来保存用户的登录信息,Cookie和Session都有自己的生命周期,所以在平时做登录的时候我都不加思索的将用户的信息放到session中,然后根据session中是否存放用户的信息来判断用户是否登录或者是否具有某个权限,而忽略了其运行机理。那么session到底是什么时候创建的呢:
现在有很多客户端,每个客户端上面有很多浏览器,比如说现在用户通过 客户端A 的浏览器a1去访问服务器,可问题是现在客户端并不知道服务器是否启动,是否能够被访问,那么怎样解决这个问题?这就是Session的作用了,当客户端访问服务器的时候,如果服务器启动,则服务器创建一个session用来保存客户端的信息,并将session的ID写到对应客户端浏览器a1的cookie中,当a1再次发送请求的时候,每次都将cookie中的sessionID传到服务器,服务器就可以通过sessionID来判断是哪个客户端发送过来的请求;如果服务器没有启动,客户端也就得不到sessionID自然也就不能继续请求了。
过程如下图:
现在再来想想,为什么需要cookie和session?从上图可以看出来cookie和session的作用不就是保持了客户端与服务器端的连接吗,http本来是无状态的协议,但是通过cookie和session的作用就可以实现客户端与服务器端状态连接的保持。
再往下想想,客户端与服务器端的通信,也就是两边各自不同的端口之间的通信,而每个sessionId相当于是记录了一个与之对应的一个IP(客户端)和端口(浏览器),这样也就是为什么网络通信经常强调套接字了,说白了还是网络之间的通信。
这样也就解释了为什么我们在同一个浏览器两个窗口上访问同一个服务器时候只能使用一个账户了,例如在窗口1使用账户1中登陆后,再打开窗口2使用账户2登陆,再刷新窗口1发现窗口1上显示的是账户2的信息,因为浏览器的端口号只有一个,不管使用窗口1还是窗口2都对应的是服务器上的同一个session:
如图所示的过程可以在同一浏览器上测试,我在淘宝上测试得出的结论是正确的,当然如果当我们在同一客户端上使用不同的浏览器登陆不同的用户又是可以分别得到各自用户登陆的信息,这也就不难解释了,不同浏览器访问服务器时对应的sessionId不同,对应的端口也不同,所以还是符合网络通信的原理,这样理解应该没有问题。
掌握了基本原理之后,再来处理网站在线人数统计的问题也就变得简单了:首先我们在application中配置一个全局变量onlineNumber用来计数,然后对session进行监听,每当创建session的时候将application中的变量加一,结果发现这样是有问题的(上面已经说了,当同一客户端使用不同的浏览器进行访问时也会创建session),所以除了监听session的创建之外,我们还需要用一个集合来保留客户端的唯一信息(可以使用IP),每次创建session的时候去的访问的Ip如果集合中不存在Ip则加入Ip并将全局变量加1,后来发现即便是这样也不能精确的统计,因为session是有存在时间的,如果用户登录后没有关闭浏览器而是跳到别的页面了,这样就带来了统计的误差,解决办法是设置session的有效时间(timeOut),当时间到后便会销毁session,同时将application全局变量减一,也就实现了网上在线人数的初步统计。当然也许这样还不是非常精确,但是考虑到统计在线人数只是为了大致了解当前服务器承受的访问压力而作出调整,所以这样实现已经能达到目的了。
有了思路之后要实现应该不难,只需要创建自己的监听器实现HttpSessionListener和HttpSessionAttributeListener即可,前者监听session的创建和销毁,后者主要是监听session属性的变化,代码大致如下:
public class OnlineUserListener implements HttpSessionListener,
HttpSessionAttributeListener {
@Override
public void attributeAdded(HttpSessionBindingEvent arg0) {
System.out.println("attributeAdded.....");
}
@Override
public void attributeRemoved(HttpSessionBindingEvent arg0) {
System.out.println("attributeRemoved.....");
}
@Override
public void attributeReplaced(HttpSessionBindingEvent arg0) {
System.out.println("attributeReplaced.....");
}
@Override
public void sessionCreated(HttpSessionEvent arg0) {
System.out.println("createSession.....");
Map<String,Object> application=ActionContext.getContext().getApplication();
//从web配置文件中得到属性的值,如果创建一个session就加一,销毁减一
int onlineNumber=Integer.parseInt(application.get("onlineNumber").toString())+1;
application.put("onlineNumber", onlineNumber);
}
@Override
public void sessionDestroyed(HttpSessionEvent arg0) {
System.out.println("deleteSession.....");
Map<String,Object> application=ActionContext.getContext().getApplication();
int onlineNumber=Integer.parseInt(application.get("onlineNumber").toString())-1;
application.put("onlineNumber", onlineNumber);
}
接下来就是Cookie的用法了,有登陆页面:
用户选择记住的时候,应该创建cookie保存用户密码,对应代码:
//判断用户Cookie是否存在 存放用户信息的Cookie名字 : cookieInfo
public boolean isExistsCookie(){
boolean result=false;
HttpServletRequest request=ServletActionContext.getRequest();
Cookie[] cookies=request.getCookies();
for(Cookie c:cookies){
if("cookieInfo".equals(c.getName())){
result=true;
break;
}
}
return result;
}
//根据用户的选择添加Cookie
public void addCookie(){
if(remember!=null && !isExistsCookie()){
HttpServletResponse response=ServletActionContext.getResponse();
Cookie newCookie=new Cookie("cookieInfo",userPass);
newCookie.setMaxAge(60*60*24);
response.addCookie(newCookie);
}
}
//根据用户的请求清楚Cookie
public void deleteCookie(){
if(remember==null && isExistsCookie()){
System.out.println("deleteCookie");
HttpServletResponse response=ServletActionContext.getResponse();
HttpServletRequest request=ServletActionContext.getRequest();
Cookie[] cookies=request.getCookies();
for(Cookie c:cookies){
if("cookieInfo".equals(c.getName())){
c.setMaxAge(0); //设置生命为0 浏览器会清除Cookie
response.addCookie(c);
break;
}
}
}
}
从前台上得到cookie中的信息 : jsp代码:
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>用户登录</title>
<script type="text/javascript">
// 取得Cookie的值
function getCookie(cname){
var name = cname + "=";
var ca = document.cookie.split(';');
for(var i=0; i<ca.length; i++) {
var c = ca[i].trim();
if (c.indexOf(name)==0) return c.substring(name.length,c.length);
}
return "";
}
//给密码赋值
function setPass(){
var pass=document.getElementById("userPass");
pass.value=getCookie("cookieInfo");
}
</script>
</head>
<body οnlοad="setPass()">
<form method="post" action="loginAction">
<table>
<tr>
<td>用户名:</td>
<td><input type="text" name="userName"></td>
</tr>
<tr>
<td>密码:</td>
<td><input type="text" name="userPass" id="userPass"></td>
</tr>
<tr>
<td>记住密码</td>
<td><input type="checkbox" name="remember"></td>
</tr>
<tr>
<td>用户类型</td>
<td><input type="radio" name="userType" value="0">普通用户 <input type="radio" name="userType" checked="checked" value="1">管理员</td>
</tr>
<tr>
<td><input type="submit" value="登录"></td>
<td><input type="button" name="register" value="注册"></td>
</tr>
</table>
</form>
<div>
<%@include file="footer.jsp" %>
</div>
</body>
</html>
这样就完成了session和cookie使用的总结,网站在线人数的统计思路也出来了代码实现很简单,网上很多人也总结了,可以用来借鉴,我主要是总结Cookie和Session的使用原理。