14. 实例 - 统计服务器在线用户 - JSP 和 监听器 的实现

使用监听器实现在线客户统计

统计连接在应用上的客户端数量。客户端的唯一标识就是 IP,只需要对连接到服务器上
的 IP 数量进行统计,就可统计出客户端的数量。很多人都是只监听了session,即当session创建时,在线客户端的数量就+1,session销毁时,在线客户端的数量就-1,这种统计的方法不正确,因为用户在同一台机器上打开两个不同的浏览器访问系统时,系统会创建两个session对象,如果按照上面方式统计的话当前在线客户有两个,而实际上只有一个,因此需要通过客户端的ip地址来判断的方式更加准确。

 

 

 

 

这是分析图 已经很明确了,但是要更明确 应该懂得 Session 的 生存 和 销毁。

 

 

我们代码思路大体这样写:

主要思路是 用 Ip【一个用户】 来装Session【不同浏览器】,Map容器【Map不允许重复 且 映射型容器】

1.首先注册 ServletContext的监听器,因为服务器启动ServletContext才会生成,且 Servlet 只有一个,所以它创建时,我们就初始化容器【装Session】,将容器放入 ServletContext。

2.  【这里的List 是对应 一个用户【IP】的! 别混淆!】  监听Request 的创建,然后我们获取到他的 IP,顺便将他的IP放入Session中【Session销毁用的到】然后根据IP获取到它的List【Session容器】,判断一下List是不是为null,如果是null的话 证明第一次访问【无装载记录】 ,第一次访问我们将他的List【Session容器】 赋予地址。

其次我们获取到他 当前的Session【第一次访问Session就会自动生成】,然后遍历他的 list ,如果 当前Session 和 list中 任意一个 的Session 相同,证明当前用户还在会话【没掉线】,那就Return处理,

如果 遍历 list 都没找到当前Session 证明 当前 Session 是 最新添加的,我们将其放入 list 中,然后我们更新Map中的List,再更新Servlet中的Map。 【 这里的更新分别指的是替换【Map 替换】 和 【ServletContext 的 Attribute 替换】 】。

3.监听Session的销毁

Session销毁【失效】的默认时间是30 分钟,你要自己去设置他的超时时间,优先级也是范围小 到 范围大的! 这里推荐点击注销的时候 用 java 代码直接 杀掉 Session。

当Session销毁时,证明用户的浏览器掉线了,所以我们获取到被销毁的Session对象,然后根据 IP 【Request监听里面已经设置】 获取到List,然后把这个失效的Session对象 从 List中移除,

然后我们判断list是不是为空,size = 0?  如果size 是 0 的话 我们直接把 ip 从 Map 中 移除, 证明整个用户直接掉线了。 最后记得要更新!  【这里的更新分别指的是替换【Map 替换】 和 【ServletContext 的 Attribute 替换】 】。

4.监听记得在XML配置中加配置

 

代码:

MyServletContextListener.java:

package onlineNumber;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.servlet.ServletContext;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import javax.servlet.http.HttpSession;

/**
 * 监听ServletContext    
 * @author Bi-Hu
 *
 */
public class MyServletContextListener implements ServletContextListener {

    //当ServletContext 创建时 我们做:
    @Override
    public void contextInitialized(ServletContextEvent sce) {
    //首先新建IP容器Map
     Map<String, List<HttpSession>> ipMap = new HashMap<>();    
     
     //然后我们    获取Context -> 把ip容器放入Map
     ServletContext sc = sce.getServletContext();
     sc.setAttribute("ipMap", ipMap);
    }
    
    

}

 

MyRequestListener.java

package onlineNumber;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import javax.servlet.ServletContext;
import javax.servlet.ServletRequestEvent;
import javax.servlet.ServletRequestListener;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;

/**
 * 监视Request 当Request创建时 我们根据IP判断是不是新访问,如果是新建一个List给他装Session 然后更新到Map【关键】 不是的话 直接Return
 * @author Bi-Hu
 *
 */
public class MyRequestListener implements ServletRequestListener {

    
    @Override
    public void requestInitialized(ServletRequestEvent sre) {
        //获取Request
        HttpServletRequest request = (HttpServletRequest) sre.getServletRequest();
        
        //获取ipMap
        ServletContext sc = request.getServletContext();
        Map<String, List<HttpSession>> ipMap = (Map<String, List<HttpSession>>)sc.getAttribute("ipMap");
        
        //获取ip 然后根据ip找list 看下是否第一次登录 或 重新登录
         String Ip = request.getRemoteAddr();
         System.out.println(Ip);
        
        
         List<HttpSession> list = ipMap.get(Ip);
         if(list == null) {
             list = new ArrayList<HttpSession>();
         }
         

         
         //获取当前Session【你获取了 不存在会新建】,然后判断list中有没有你这个Session ,如果没有代表你是新Session 如果存在就结束
         HttpSession CurrentSession = request.getSession();
         for(HttpSession s : list) {
             if(s == CurrentSession) {    
                //如果重复 代表你还在会话中 
                 return;
             }
         }
         
         //如果不存在 就执行到这里了 代表你是新建的Session 那么就会进行更新:【这一步很重要!!】
         //把新建的Session放入list
         list.add(CurrentSession);
         ipMap.put(Ip, list);                //更新list
         sc.setAttribute("ipMap", ipMap);    //更新ipMap
         
         //顺便把当前Ip放入 Session中
         CurrentSession.setAttribute("ip", Ip);        //销毁Session时用的到
        System.out.println("Ip为:"+ Ip +"中的 Session 个数: " + ipMap.get(Ip).size() + "个");
        
    }

    
}

MySessionListener.java

package onlineNumber;

import java.util.List;
import java.util.Map;

import javax.servlet.ServletContext;
import javax.servlet.http.HttpSession;
import javax.servlet.http.HttpSessionEvent;
import javax.servlet.http.HttpSessionListener;
import javax.websocket.Session;

/**
 *     监视Session销毁时 你销毁 我就把你删除 ,然后判断list是否为空 为空的话 用户就离线了 所以我们把List给Kill掉
 * @author Bi-Hu
 *
 */
public class MySessionListener implements HttpSessionListener {

    @Override
    public void sessionDestroyed(HttpSessionEvent se) {
        //先获取到他的Session
        HttpSession CurrentSession = se.getSession();
        ServletContext sc = CurrentSession.getServletContext();
        String CurrentIp = (String) CurrentSession.getAttribute("ip");

        
        
        //然后我们根据IP找到他的 List 然后在List中移除这个Session【销毁的】
        Map<String, List<HttpSession>> ipMap = (Map<String, List<HttpSession>>) sc.getAttribute("ipMap");
        List<HttpSession> list = ipMap.get(CurrentIp);
        list.remove(CurrentSession);
        
        System.out.println("ID 为" + se.getSession().getId() + " 的 Session 销毁了");
        System.out.println("Ip为:"+ CurrentIp +"中的 Session 个数: " + ipMap.get(CurrentIp).size() + "个");
        
        //然后我们判断list还有么有元素 没有的话 证明用户离线了 那我们把它的Id 删除 然后更新 就收工
        if(list.size() == 0) {
            ipMap.remove(CurrentIp);
        }else {
            ipMap.put(CurrentIp, list);        //更新List
        }
        
        sc.setAttribute(CurrentIp, ipMap);    //总更新【重要】
        System.out.println("还剩在线用户:" + ipMap.size());
        
    }

    
}

 

我在这里设置了 1分钟Session 失效  这样就很快看的出 在线 /  离线 的对比了

 

电脑的火狐 会用 127.0.0.1  谷歌 和 其他浏览器均是0.0.0.0.1

所以会有两个用户 Session 的对比也能看得出来 目标完成。

 成功图:

 

 

谢谢。 

 

posted @ 2021-06-08 09:44  咸瑜  阅读(225)  评论(0编辑  收藏  举报