亚洲视频二区_亚洲欧洲日本天天堂在线观看_日韩一区二区在线观看_中文字幕不卡一区

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

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

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

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

 

  1.  

     

    ServletRequestListener:用于監(jiān)聽(tīng)請(qǐng)求的監(jiān)聽(tīng)接口

     

  2.  

     

    HttpSessionListener:用于監(jiān)聽(tīng)會(huì)話的監(jiān)聽(tīng)接口

     

  3.  

     

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

     

錯(cuò)誤的統(tǒng)計(jì)辦法

 

監(jiān)聽(tīng)Request域

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

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
//請(qǐng)求被初始化 Request
public void requestInitialized(ServletRequestEvent sre) {
//獲取全局域
sc = sre.getServletContext();
//將count從全局域中獲取出來(lái)
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被初始化的時(shí)候創(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>

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

監(jiān)聽(tīng)Session域

在第二次監(jiān)聽(tīng)Session域之后,發(fā)現(xiàn)每次刷新頁(yè)面后不改變count但是在啟動(dòng)不同的瀏覽器后count++會(huì)實(shí)現(xiàn),但是,這樣做并不是我們要統(tǒng)計(jì)的在線人數(shù),所以此種做法錯(cuò)誤。由于代碼只是將原來(lái)寫(xiě)在Request監(jiān)聽(tīng)器中的代碼轉(zhuǎn)移到Session監(jiān)聽(tīng)器中,所以其他沒(méi)變的代碼將不重復(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)對(duì)話產(chǎn)生時(shí)激活此方法
public void sessionCreated(HttpSessionEvent se) {
sc = se.getSession().getServletContext();
count = (Integer) sc.getAttribute("count");
count++;
sc.setAttribute("count",count);
}
}

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

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

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

 

  •  

    如何獲取用戶(hù)的IP?

     

  •  

    IP將如何存儲(chǔ)?

     

  •  

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

     

 

現(xiàn)在來(lái)解決這些問(wèn)題:

 

  •  

    只能從請(qǐng)求中獲取

     

  •  

    通過(guò)2、3問(wèn)題,我們想到了集合(List),因?yàn)榧喜粌H可以存儲(chǔ)任何字符串,還可以通過(guò)遍歷來(lái)判斷之前是否有重復(fù)的IP出現(xiàn)。

     

 

到了這里又冒出來(lái)一個(gè)問(wèn)題集合(List)放到哪個(gè)域里呢?

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被初始化的時(shí)候創(chuàng)建
public void contextInitialized(ServletContextEvent sce) {
//創(chuàng)建一個(gè)鏈表來(lái)存儲(chǔ)IP
List ips = new ArrayList<>();
sc = sce.getServletContext();
//將創(chuàng)建好的鏈表對(duì)象,放到Application域中
sc.setAttribute("ips",ips);
}
}

由于IP只能在Request域中獲取,所以遍歷判斷在Request域中進(jìn)行。公眾 號(hào)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
//請(qǐng)求被初始化 Request
public void requestInitialized(ServletRequestEvent sre) {
//從請(qǐng)求域中獲取IP
sr = (HttpServletRequest) sre.getServletRequest();
clientIp = sr.getRemoteAddr();
session = sr.getSession();
session.setAttribute("clientIp",clientIp);

//測(cè)試
// 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);
}
}

因?yàn)橐y(tǒng)計(jì)在線人數(shù),所以要設(shè)置退出按鈕,點(diǎn)擊退出按鈕之后,因?yàn)橐獜腖ist域中移除,所以使用Session域監(jiān)聽(tīng)器來(lái)判斷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
//因?yàn)镾ession獲取不到Request
//一個(gè)Session包含多個(gè)Request
//一個(gè)Request只對(duì)應(yīng)一個(gè)Session 所以獲取不到,這時(shí)只能先從Request域中獲取到的ips,放置到Session域
//然后從Session 域中讀取
ips.remove(clientIp);
// session一失效就馬上將此IP從鏈表中移除是錯(cuò)誤的
//應(yīng)該看此IP是否有另外的回話存在,如果有的話不能刪除
}
}

此處代碼是頁(yè)面點(diǎn)擊關(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 {
//從域中獲取一個(gè)session,設(shè)置為false 如果域中存在一個(gè)session,則直接獲取,如果不存在,則返回一個(gè)空的session
session = request.getSession(false);
if (session != null){
//使session失效
session.invalidate();
//失效后,需要進(jìn)行的操作,List鏈表中需要減去,用到了Session域監(jiān)聽(tīng)器
}
}

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

在jsp頁(yè)面進(jìn)行讀取的時(shí)候,因?yàn)閕ps是以List鏈表的形式存在的,所以要想判斷當(dāng)前在線人數(shù),所以必須要判斷鏈表的長(zhǎng)度,所以是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>

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

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

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

2、127.0.0.1

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

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

 

  •  

    localhost等于127.0.0.1,不過(guò)localhost是域名,127.0.0.1是IP地址。

     

  •  

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

     

  •  

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

     

  •  

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

     

 

3、IPv4地址:192.168.1.110

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

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

最完美的代碼

所以在點(diǎn)擊退出登錄的按鈕之后,不可以直接將IP移除,要判斷有沒(méi)有另外的回話存在,如果有另外的回話存在,此IP是不可以刪掉的,問(wèn)題由此變的復(fù)雜了,因?yàn)檫€要統(tǒng)計(jì)此IP所發(fā)出的會(huì)話有多少。

整體思路:

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

通過(guò)調(diào)用Map的get方法,將當(dāng)前IP最為參數(shù),將可以獲取到他所激發(fā)的會(huì)話集合。但是,此集合可能為空,因?yàn)橛锌赡墚?dāng)前IP一次也沒(méi)有訪問(wèn)此頁(yè)面,所以在List為空的時(shí)候好要?jiǎng)?chuàng)建一個(gè)ArrayList來(lái)存放sessions,然后將變化后的List重新寫(xiě)回到Map,再將變化后的Map寫(xiě)回到全局域中 。這樣創(chuàng)建過(guò)程基本完成。

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

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

然后,要考慮的問(wèn)題是,每次刷新頁(yè)面后sessions的個(gè)數(shù)會(huì)增加,這是錯(cuò)誤的,原因是什么?

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

這樣添加就沒(méi)問(wèn)題了

Servlet域中添加Map

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

請(qǐng)求,在請(qǐng)求中,因?yàn)閷?dāng)前Session 對(duì)象存放到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
//請(qǐng)求被初始化 Request
public void requestInitialized(ServletRequestEvent sre) {
//從請(qǐng)求域中獲取IP
sr = (HttpServletRequest) sre.getServletRequest();
clientIp = sr.getRemoteAddr();
currentSession = sr.getSession();
//將當(dāng)前Session 對(duì)象存放到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 對(duì)象,若有則不添加,若沒(méi)有則添加
for (HttpSession session :
sessions) {
if (session == currentSession)
return;
}
sessions.add(currentSession);

//將變化過(guò)的List重新寫(xiě)回到Map
map.put(clientIp,sessions);
//再將變化的Map寫(xiě)回到全局域中
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被初始化的時(shí)候創(chuàng)建
public void contextInitialized(ServletContextEvent sce) {
//創(chuàng)建一個(gè)Map,key為IP,value為該IP上所發(fā)出的會(huì)話的對(duì)象
Map> map = new HashMap<>();
sc = sce.getServletContext();
//將map放到全局域中
sc.setAttribute("map",map);
}
}

Session監(jiān)聽(tīng)器

接下來(lái)剖析Session的刪除工作,獲取當(dāng)前Session對(duì)象,這里有之前傳遞過(guò)來(lái)的IP,在進(jìn)行刪除操作的時(shí)候,要注意此處,刪除的是List中的sessions,刪除之后,還要判斷其IP的是否要?jiǎng)h除,如果List中沒(méi)有該元素,則說(shuō)明當(dāng)前IP所發(fā)出的會(huì)話全部關(guān)閉,就可以從map中將當(dāng)前IP對(duì)應(yīng)的Entry對(duì)象刪除,否則,當(dāng)前IP所發(fā)出的會(huì)話任存在,那么使用put方法將變化過(guò)的List寫(xiě)回到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對(duì)象
sessions.remove(currentSession);
//如果List中沒(méi)有該元素,則說(shuō)明當(dāng)前IP所發(fā)出的會(huì)話全部關(guān)閉,就可以從map中
//將當(dāng)前IP對(duì)應(yīng)的Entry對(duì)象刪除
//若List中仍有元素,當(dāng)前IP所發(fā)出的會(huì)話任存在,那么將變化過(guò)的List寫(xiě)回到map
if (sessions.size() == 0){
map.remove(clientIp);
}else {
map.put(clientIp,sessions);
}
sc.setAttribute("map",map);
}
}

因?yàn)樘幚淼耐顺龅捻?yè)面/logoutServlet不需要做任何不同的處理,所以這里將不再重復(fù)。

因?yàn)樵趈sp用到了JSP標(biāo)準(zhǔn)庫(kù),所以到導(dǎo)兩個(gè)包。

<%@ 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>

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

 

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

 

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

最近有很多人問(wèn),有沒(méi)有讀者交流群!加入方式很簡(jiǎn)單,公眾號(hào)Java精選,回復(fù)“加群”,即可入群!

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

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

網(wǎng)友整理

注冊(cè)時(shí)間:

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

  • 51998

    網(wǎng)站

  • 12

    小程序

  • 1030137

    文章

  • 747

    會(huì)員

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

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

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

答題星2018-06-03

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

全階人生考試2018-06-03

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

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

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

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

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

體育訓(xùn)練成績(jī)?cè)u(píng)定2018-06-03

通用課目體育訓(xùn)練成績(jī)?cè)u(píng)定