日日操夜夜添-日日操影院-日日草夜夜操-日日干干-精品一区二区三区波多野结衣-精品一区二区三区高清免费不卡

公告:魔扣目錄網(wǎng)為廣大站長提供免費收錄網(wǎng)站服務(wù),提交前請做好本站友鏈:【 網(wǎng)站目錄:http://www.ylptlb.cn 】, 免友鏈快審服務(wù)(50元/站),

點擊這里在線咨詢客服
新站提交
  • 網(wǎng)站:51998
  • 待審:31
  • 小程序:12
  • 文章:1030137
  • 會員:747

統(tǒng)計用戶在線人數(shù)

在統(tǒng)計用戶在人數(shù)的時候,我們用到了監(jiān)聽器,監(jiān)聽器大致分為以下三種:

 

  1.  

     

    ServletRequestListener:用于監(jiān)聽請求的監(jiān)聽接口

     

  2.  

     

    HttpSessionListener:用于監(jiān)聽會話的監(jiān)聽接口

     

  3.  

     

    ServletContextListener:用于監(jiān)聽?wèi)?yīng)用的回話接口

     

錯誤的統(tǒng)計辦法

 

監(jiān)聽Request域

這種統(tǒng)計辦法是錯誤的認(rèn)為每次刷新頁面后進(jìn)行進(jìn)行一次的count++運算

import JAVAx.servlet.*;
import javax.servlet.annotation.WebListener;
import javax.servlet.http.HttpSessionAttributeListener;
import javax.servlet.http.HttpSessionEvent;
import javax.servlet.http.HttpSessionListener;
import javax.servlet.http.HttpSessionBindingEvent;

 

@WebListener()
public class MyRequestListener implements ServletRequestListener{
private ServletContext sc;
private Integer count;
@Override
//請求被初始化 Request
public void requestInitialized(ServletRequestEvent sre) {
//獲取全局域
sc = sre.getServletContext();
//將count從全局域中獲取出來
count = (Integer) sc.getAttribute("count");
System.out.println(count);
count++;
System.out.println(count);
sc.setAttribute("count",count);
}
}

import javax.servlet.ServletContext;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import javax.servlet.annotation.WebListener;
import javax.servlet.http.HttpSessionAttributeListener;
import javax.servlet.http.HttpSessionEvent;
import javax.servlet.http.HttpSessionListener;
import javax.servlet.http.HttpSessionBindingEvent;

@WebListener()
public class MyServletContextListener implements ServletContextListener{
private ServletContext sc;
@Override
//Application被初始化的時候創(chuàng)建
public void contextInitialized(ServletContextEvent sce) {
Integer count = 0;
//獲取全局域
sc = sce.getServletContext();
//將count放入到全局域中
sc.setAttribute("count",count);
}
}

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
$Title$title>
head>

You are the ${applicationScope.count} customer to visit. h1>center>
body>
html>

這種錯誤地做法導(dǎo)致的是每刷新一次頁面 就會導(dǎo)致count進(jìn)行累加操作,最終產(chǎn)生錯誤的在線人數(shù),所以此時想到不應(yīng)該監(jiān)聽Request域,而應(yīng)該監(jiān)聽Session域。

監(jiān)聽Session域

在第二次監(jiān)聽Session域之后,發(fā)現(xiàn)每次刷新頁面后不改變count但是在啟動不同的瀏覽器后count++會實現(xiàn),但是,這樣做并不是我們要統(tǒng)計的在線人數(shù),所以此種做法錯誤。由于代碼只是將原來寫在Request監(jiān)聽器中的代碼轉(zhuǎn)移到Session監(jiān)聽器中,所以其他沒變的代碼將不重復(fù)。

import javax.servlet.ServletContext;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import javax.servlet.annotation.WebListener;
import javax.servlet.http.HttpSessionAttributeListener;
import javax.servlet.http.HttpSessionEvent;
import javax.servlet.http.HttpSessionListener;
import javax.servlet.http.HttpSessionBindingEvent;

 

@WebListener()
public class MySessionListener implements HttpSessionListener{

private ServletContext sc;
private Integer count;

@Override
//當(dāng)對話產(chǎn)生時激活此方法
public void sessionCreated(HttpSessionEvent se) {
sc = se.getSession().getServletContext();
count = (Integer) sc.getAttribute("count");
count++;
sc.setAttribute("count",count);
}
}

這時我們發(fā)現(xiàn)對于在線人數(shù)的統(tǒng)計,不是網(wǎng)頁訪問的次數(shù),也不是瀏覽器打開的個數(shù),對需求的理解的錯誤理解。所以正確的做法是統(tǒng)計其IP的數(shù)量,這樣的話,不管你在一臺電腦上開啟多少客戶端,都會只有一個。

正確的統(tǒng)計方法

統(tǒng)計其IP的數(shù)量,將IP的數(shù)量作為當(dāng)前的在線人數(shù),那么如何統(tǒng)計IP的數(shù)量呢?這樣將會導(dǎo)出以下問題:

 

  •  

    如何獲取用戶的IP?

     

  •  

    IP將如何存儲?

     

  •  

    如何判斷IP之前已經(jīng)存在?

     

 

現(xiàn)在來解決這些問題:

 

  •  

    只能從請求中獲取

     

  •  

    通過2、3問題,我們想到了集合(List),因為集合不僅可以存儲任何字符串,還可以通過遍歷來判斷之前是否有重復(fù)的IP出現(xiàn)。

     

 

到了這里又冒出來一個問題集合(List)放到哪個域里呢?

ServletContext域

import javax.servlet.ServletContext;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import javax.servlet.annotation.WebListener;
import javax.servlet.http.HttpSessionAttributeListener;
import javax.servlet.http.HttpSessionEvent;
import javax.servlet.http.HttpSessionListener;
import javax.servlet.http.HttpSessionBindingEvent;
import java.util.ArrayList;
import java.util.List;

 

@WebListener()
public class MyServletContextListener implements ServletContextListener{
private ServletContext sc;

@Override
//Application被初始化的時候創(chuàng)建
public void contextInitialized(ServletContextEvent sce) {
//創(chuàng)建一個鏈表來存儲IP
List ips = new ArrayList<>();
sc = sce.getServletContext();
//將創(chuàng)建好的鏈表對象,放到Application域中
sc.setAttribute("ips",ips);
}
}

由于IP只能在Request域中獲取,所以遍歷判斷在Request域中進(jìn)行。公眾 號Java精選,回復(fù)java面試,獲取面試資料,支持在線刷題。

import javax.servlet.*;
import javax.servlet.annotation.WebListener;
import javax.servlet.http.*;
import java.util.List;

 

@WebListener()
public class MyRequestListener implements ServletRequestListener{

private HttpServletRequest sr;
private String clientIp;
private ServletContext sc;
private List ips;
private HttpSession session;

@Override
//請求被初始化 Request
public void requestInitialized(ServletRequestEvent sre) {
//從請求域中獲取IP
sr = (HttpServletRequest) sre.getServletRequest();
clientIp = sr.getRemoteAddr();
session = sr.getSession();
session.setAttribute("clientIp",clientIp);

//測試
// System.out.println("clientIp = "+ clientIp);
//獲取Application域中的List
sc = sre.getServletContext();
ips = (List) sc.getAttribute("ips");
//遍歷ips
for (String ip :
ips) {
if (clientIp.equals(ip))
return;
}
ips.add(clientIp);
sc.setAttribute("ips",ips);
}
}

因為要統(tǒng)計在線人數(shù),所以要設(shè)置退出按鈕,點擊退出按鈕之后,因為要從List域中移除,所以使用Session域監(jiān)聽器來判斷session回話的關(guān)閉

import javax.servlet.ServletContext;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import javax.servlet.annotation.WebListener;
import javax.servlet.http.*;
import java.util.List;

 

@WebListener()
public class MySessionListener implements HttpSessionListener{

private ServletContext sc;
private List ips;
private HttpSession session;
private Object clientIp;

@Override
public void sessionDestroyed(HttpSessionEvent se) {
sc = se.getSession().getServletContext();
ips = (List) sc.getAttribute("ips");
session = se.getSession();
clientIp = session.getAttribute("clientIp");
//刪除ip,如何獲取IP,但是不可以從session獲取到IP
//因為Session獲取不到Request
//一個Session包含多個Request
//一個Request只對應(yīng)一個Session 所以獲取不到,這時只能先從Request域中獲取到的ips,放置到Session域
//然后從Session 域中讀取
ips.remove(clientIp);
// session一失效就馬上將此IP從鏈表中移除是錯誤的
//應(yīng)該看此IP是否有另外的回話存在,如果有的話不能刪除
}
}

此處代碼是頁面點擊關(guān)閉后,激活的退出方法

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;

 

@WebServlet(name = "LogoutServlet",urlPatterns = "/logoutServlet")
public class LogoutServlet extends HttpServlet {

private HttpSession session;

protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//從域中獲取一個session,設(shè)置為false 如果域中存在一個session,則直接獲取,如果不存在,則返回一個空的session
session = request.getSession(false);
if (session != null){
//使session失效
session.invalidate();
//失效后,需要進(jìn)行的操作,List鏈表中需要減去,用到了Session域監(jiān)聽器
}
}

protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doPost(request,response);
}
}

在jsp頁面進(jìn)行讀取的時候,因為ips是以List鏈表的形式存在的,所以要想判斷當(dāng)前在線人數(shù),所以必須要判斷鏈表的長度,所以是applicationScope.ips.size()

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
$Title$title>
head>

You are the ${applicationScope.ips.size()} customer to visit. h1>
安全退出
a>h3>
center>
body>
html>

好了?,這時候,程序?qū)懲炅耍绾闻袛嗄兀?/code>

此時,我們的程序是部署在本地的Tomcat上的,對于本臺電腦,只有一個IP,如何實現(xiàn)多個IP呢?其實啊我們的電腦可以有三個IP,在訪問服務(wù)器的時候,服務(wù)器的IP多寫幾個,相當(dāng)于本機的IP多出來幾個。是哪三個IP呢?

1、默認(rèn)clientIp :0:0:0:0:0:0:0:1

2、127.0.0.1

這時大家可能會問127.0.0.1和localhost有什么區(qū)別呢,其實在這里要區(qū)分三個概念:

localhost、127.0.0.1 和 本機IP之間的區(qū)別:

 

  •  

    localhost等于127.0.0.1,不過localhost是域名,127.0.0.1是IP地址。

     

  •  

    localhost和127.0.0.1不需要聯(lián)網(wǎng),都是本機訪問。

     

  •  

    本機IP需要聯(lián)網(wǎng),本機IP是本機或外部訪問, 本機 IP 就是本機對外放開訪問的IP地址,這個網(wǎng)址就 是與物理網(wǎng)卡綁定的IP地址。

     

  •  

    面試寶典:https://www.yoodb.com

     

 

3、IPv4地址:192.168.1.110

這樣就很完美的實現(xiàn)了本地三個IP的測試。

寫到這里,似乎已經(jīng)可以簡單的測試當(dāng)前在線人數(shù),也許仔細(xì)的人會發(fā)現(xiàn)在Session域被銷毀的方法中的注釋中發(fā)現(xiàn)一些貓膩。大家可以仔細(xì)想想,如果客戶端用不同的瀏覽器,相同的IP去訪問呢?點擊退出后,會不會出現(xiàn)錯誤情況呢?答案是會的。演示結(jié)果如下圖

最完美的代碼

所以在點擊退出登錄的按鈕之后,不可以直接將IP移除,要判斷有沒有另外的回話存在,如果有另外的回話存在,此IP是不可以刪掉的,問題由此變的復(fù)雜了,因為還要統(tǒng)計此IP所發(fā)出的會話有多少。

整體思路:

在全局域中,將不是直接將iP存放在List的鏈表中,而是以一個Map的形式存在,Map的鍵為String類型,Key為List類型,List中存放的是當(dāng)前IP所激發(fā)的會話對象,這樣就可以統(tǒng)計,一個IP觸發(fā)的sessions有多少個。

通過調(diào)用Map的get方法,將當(dāng)前IP最為參數(shù),將可以獲取到他所激發(fā)的會話集合。但是,此集合可能為空,因為有可能當(dāng)前IP一次也沒有訪問此頁面,所以在List為空的時候好要創(chuàng)建一個ArrayList來存放sessions,然后將變化后的List重新寫回到Map,再將變化后的Map寫回到全局域中 。這樣創(chuàng)建過程基本完成。

然后考慮銷毀過程,IP還需方法放到Session域中,當(dāng)session被銷毀的時候,應(yīng)該把當(dāng)前Session從List 中刪除,但是Map中此sessions對應(yīng)的IP可是不能直接刪,要判斷List中的sessions的個數(shù)(Entry對象),個數(shù)為1的時候才可以刪除,不然就不可以刪除。

所以,要將當(dāng)前IP通過Request域存放到當(dāng)前Session域中,

然后,要考慮的問題是,每次刷新頁面后sessions的個數(shù)會增加,這是錯誤的,原因是什么?

答案是,因為在存放sessions的時候,創(chuàng)建數(shù)組直接進(jìn)行的添加,這樣的話,每次一刷新頁面,就會導(dǎo)致sessions的添加,所以在此之前應(yīng)該判斷,sessions中是否有此session,有的話直接跳出。

這樣添加就沒問題了

Servlet域中添加Map

在Map中,需要使用鍵值對的方式,Key為IP,Value為List,那么List中存放什么呢?存放的是此IP發(fā)出的所有回話的HttpSession的對象,所以List的泛型是HttpSession。

請求,在請求中,因為將當(dāng)前Session 對象存放到List中, List在Map中,Map在全局域中,所以首先得從全局域獲取到Map,然后,從Map中獲取由當(dāng)前IP所發(fā)出的所有Session的組成的List,判斷當(dāng)前的List是否為NULL,若為NULL,則創(chuàng)建List,否則,將當(dāng)前SessioncurrentSession放入List中。

import javax.servlet.*;
import javax.servlet.annotation.WebListener;
import javax.servlet.http.*;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

 

@WebListener()
public class MyRequestListener implements ServletRequestListener{

private HttpServletRequest sr;
private String clientIp;
private ServletContext sc;
private List ips;
private HttpSession currentSession;
private Map> map;
private List sessions;

@Override
//請求被初始化 Request
public void requestInitialized(ServletRequestEvent sre) {
//從請求域中獲取IP
sr = (HttpServletRequest) sre.getServletRequest();
clientIp = sr.getRemoteAddr();
currentSession = sr.getSession();
//將當(dāng)前Session 對象存放到List中, List在Map中,Map在全局域中,
sc = sre.getServletContext();
map = (Map>) sc.getAttribute("map");
//從Map中獲取由當(dāng)前IP所發(fā)出的所有Session的組成的List
sessions = map.get(clientIp);
//判斷當(dāng)前的List是否為NULL,若為NULL,則創(chuàng)建List,否則,將當(dāng)前Session放入List
if (sessions == null){
sessions = new ArrayList<>();
}
// 遍歷List的session 對象,若有則不添加,若沒有則添加
for (HttpSession session :
sessions) {
if (session == currentSession)
return;
}
sessions.add(currentSession);

//將變化過的List重新寫回到Map
map.put(clientIp,sessions);
//再將變化的Map寫回到全局域中
sc.setAttribute("map",map);

//將當(dāng)前IP放入到當(dāng)前Session
currentSession.setAttribute("clientIp",clientIp);
}

}
ServletContext

這里將不使用ips了,所以將其刪除

import javax.servlet.ServletContext;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import javax.servlet.annotation.WebListener;
import javax.servlet.http.*;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

 

@WebListener()
public class MyServletContextListener implements ServletContextListener{
private ServletContext sc;
@Override
//Application被初始化的時候創(chuàng)建
public void contextInitialized(ServletContextEvent sce) {
//創(chuàng)建一個Map,key為IP,value為該IP上所發(fā)出的會話的對象
Map> map = new HashMap<>();
sc = sce.getServletContext();
//將map放到全局域中
sc.setAttribute("map",map);
}
}

Session監(jiān)聽器

接下來剖析Session的刪除工作,獲取當(dāng)前Session對象,這里有之前傳遞過來的IP,在進(jìn)行刪除操作的時候,要注意此處,刪除的是List中的sessions,刪除之后,還要判斷其IP的是否要刪除,如果List中沒有該元素,則說明當(dāng)前IP所發(fā)出的會話全部關(guān)閉,就可以從map中將當(dāng)前IP對應(yīng)的Entry對象刪除,否則,當(dāng)前IP所發(fā)出的會話任存在,那么使用put方法將變化過的List寫回到map。

import javax.servlet.ServletContext;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import javax.servlet.annotation.WebListener;
import javax.servlet.http.*;
import java.util.List;
import java.util.Map;

 

@WebListener()
public class MySessionListener implements HttpSessionListener{

private ServletContext sc;
private List ips;
private HttpSession currentSession;
private String clientIp;
private Map> map;
private List sessions;

@Override
public void sessionDestroyed(HttpSessionEvent se) {
sc = se.getSession().getServletContext();

currentSession = se.getSession();
clientIp = (String) currentSession.getAttribute("clientIp");
map = (Map>) sc.getAttribute("map");
//從Map中獲取List
sessions = map.get(clientIp);
//從List中刪除當(dāng)前Session對象
sessions.remove(currentSession);
//如果List中沒有該元素,則說明當(dāng)前IP所發(fā)出的會話全部關(guān)閉,就可以從map中
//將當(dāng)前IP對應(yīng)的Entry對象刪除
//若List中仍有元素,當(dāng)前IP所發(fā)出的會話任存在,那么將變化過的List寫回到map
if (sessions.size() == 0){
map.remove(clientIp);
}else {
map.put(clientIp,sessions);
}
sc.setAttribute("map",map);
}
}

因為處理的退出的頁面/logoutServlet不需要做任何不同的處理,所以這里將不再重復(fù)。

因為在jsp用到了JSP標(biāo)準(zhǔn)庫,所以到導(dǎo)兩個包。

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
$Title$title>
head>

You are the ${applicationScope.map.size()} customer to visit. h1>
安全退出
a>
h3>
${entry.key }=${entry.value.size()}
c:forEach>
h2>
center>
body>
html>

最后 測試成功,這就是一個完美的統(tǒng)計當(dāng)前用戶的在線人數(shù)。

 

作者:Wiki~ https://blog.csdn.NET/gsjwxhn/article/details/90707571

 

公眾號“Java精選”所發(fā)表內(nèi)容注明來源的,版權(quán)歸原出處所有(無法查證版權(quán)的或者未注明出處的均來自網(wǎng)絡(luò),系轉(zhuǎn)載,轉(zhuǎn)載的目的在于傳遞更多信息,版權(quán)屬于原作者。如有侵權(quán),請聯(lián)系,筆者會第一時間刪除處理!

最近有很多人問,有沒有讀者交流群!加入方式很簡單,公眾號Java精選,回復(fù)“加群”,即可入群!

(微信小程序):3000+道面試題,包含Java基礎(chǔ)、并發(fā)、JVM、線程、MQ系列、redis、Spring系列、Elasticsearch、Docker、K8s、Flink、Spark、架構(gòu)設(shè)計等,在線隨時刷題!

分享到:
標(biāo)簽:Java
用戶無頭像

網(wǎng)友整理

注冊時間:

網(wǎng)站:5 個   小程序:0 個  文章:12 篇

  • 51998

    網(wǎng)站

  • 12

    小程序

  • 1030137

    文章

  • 747

    會員

趕快注冊賬號,推廣您的網(wǎng)站吧!
最新入駐小程序

數(shù)獨大挑戰(zhàn)2018-06-03

數(shù)獨一種數(shù)學(xué)游戲,玩家需要根據(jù)9

答題星2018-06-03

您可以通過答題星輕松地創(chuàng)建試卷

全階人生考試2018-06-03

各種考試題,題庫,初中,高中,大學(xué)四六

運動步數(shù)有氧達(dá)人2018-06-03

記錄運動步數(shù),積累氧氣值。還可偷

每日養(yǎng)生app2018-06-03

每日養(yǎng)生,天天健康

體育訓(xùn)練成績評定2018-06-03

通用課目體育訓(xùn)練成績評定