Java统计用户在线人数,这样做才优雅
统计用户在线人数
在统计用户在人数的时候,我们用到了监听器,监听器大致分为以下三种:ServletRequestListener:用于监听请求的监听接口HttpSessionListener:用于监听会话的监听接口ServletContextListener:用于监听应用的回话接口错误的统计办法监听Request域
这种统计办法是错误的认为每次刷新页面后进行进行一次的count运算importjavax。servlet。;importjavax。servlet。annotation。WebListener;importjavax。servlet。http。HttpSessionAttributeListener;importjavax。servlet。http。HttpSessionEvent;importjavax。servlet。http。HttpSessionListener;importjavax。servlet。http。HttpSessionBindingEvent;WebListener()publicclassMyRequestListenerimplementsServletRequestListener{privateServletContextsc;privateIntegercount;Override请求被初始化RequestpublicvoidrequestInitialized(ServletRequestEventsre){获取全局域scsre。getServletContext();将count从全局域中获取出来count(Integer)sc。getAttribute(count);System。out。println(count);count;System。out。println(count);sc。setAttribute(count,count);}}importjavax。servlet。ServletContext;importjavax。servlet。ServletContextEvent;importjavax。servlet。ServletContextListener;importjavax。servlet。annotation。WebListener;importjavax。servlet。http。HttpSessionAttributeListener;importjavax。servlet。http。HttpSessionEvent;importjavax。servlet。http。HttpSessionListener;importjavax。servlet。http。HttpSessionBindingEvent;WebListener()publicclassMyServletContextListenerimplementsServletContextListener{privateServletContextsc;OverrideApplication被初始化的时候创建publicvoidcontextInitialized(ServletContextEventsce){Integercount0;获取全局域scsce。getServletContext();将count放入到全局域中sc。setAttribute(count,count);}}lt;pagecontentTypetexthtml;charsetUTF8languagejavahtmlheadtitleTitlelt;titleheadbodycenterh1Youarethe{applicationScope。count}customertovisit。h1centerbodyhtml
这种错误地做法导致的是每刷新一次页面就会导致count进行累加操作,最终产生错误的在线人数,所以此时想到不应该监听Request域,而应该监听Session域。监听Session域
在第二次监听Session域之后,发现每次刷新页面后不改变count但是在启动不同的浏览器后count会实现,但是,这样做并不是我们要统计的在线人数,所以此种做法错误。由于代码只是将原来写在Request监听器中的代码转移到Session监听器中,所以其他没变的代码将不重复。importjavax。servlet。ServletContext;importjavax。servlet。ServletContextEvent;importjavax。servlet。ServletContextListener;importjavax。servlet。annotation。WebListener;importjavax。servlet。http。HttpSessionAttributeListener;importjavax。servlet。http。HttpSessionEvent;importjavax。servlet。http。HttpSessionListener;importjavax。servlet。http。HttpSessionBindingEvent;WebListener()publicclassMySessionListenerimplementsHttpSessionListener{privateServletContextsc;privateIntegercount;Override当对话产生时激活此方法publicvoidsessionCreated(HttpSessionEventse){scse。getSession()。getServletContext();count(Integer)sc。getAttribute(count);count;sc。setAttribute(count,count);}}
这时我们发现对于在线人数的统计,不是网页访问的次数,也不是浏览器打开的个数,对需求的理解的错误理解。所以正确的做法是统计其IP的数量,这样的话,不管你在一台电脑上开启多少客户端,都会只有一个。正确的统计方法
统计其IP的数量,将IP的数量作为当前的在线人数,那么如何统计IP的数量呢?这样将会导出以下问题:如何获取用户的IP?IP将如何存储?如何判断IP之前已经存在?
现在来解决这些问题:只能从请求中获取通过2、3问题,我们想到了集合(List),因为集合不仅可以存储任何字符串,还可以通过遍历来判断之前是否有重复的IP出现。
到了这里又冒出来一个问题集合(List)放到哪个域里呢?
ServletContext域importjavax。servlet。ServletContext;importjavax。servlet。ServletContextEvent;importjavax。servlet。ServletContextListener;importjavax。servlet。annotation。WebListener;importjavax。servlet。http。HttpSessionAttributeListener;importjavax。servlet。http。HttpSessionEvent;importjavax。servlet。http。HttpSessionListener;importjavax。servlet。http。HttpSessionBindingEvent;importjava。util。ArrayList;importjava。util。List;WebListener()publicclassMyServletContextListenerimplementsServletContextListener{privateServletContextsc;OverrideApplication被初始化的时候创建publicvoidcontextInitialized(ServletContextEventsce){创建一个链表来存储IPListStringipsnewArrayList();scsce。getServletContext();将创建好的链表对象,放到Application域中sc。setAttribute(ips,ips);}}
由于IP只能在Request域中获取,所以遍历判断在Request域中进行。importjavax。servlet。;importjavax。servlet。annotation。WebListener;importjavax。servlet。http。;importjava。util。List;WebListener()publicclassMyRequestListenerimplementsServletRequestListener{privateHttpServletRequestsr;privateStringclientIp;privateServletContextsc;privateListStringips;privateHttpSessionsession;Override请求被初始化RequestpublicvoidrequestInitialized(ServletRequestEventsre){从请求域中获取IPsr(HttpServletRequest)sre。getServletRequest();clientIpsr。getRemoteAddr();sessionsr。getSession();session。setAttribute(clientIp,clientIp);测试System。out。println(clientIpclientIp);获取Application域中的Listscsre。getServletContext();ips(ListString)sc。getAttribute(ips);遍历ipsfor(Stringip:ips){if(clientIp。equals(ip))return;}ips。add(clientIp);sc。setAttribute(ips,ips);}}
因为要统计在线人数,所以要设置退出按钮,点击退出按钮之后,因为要从List域中移除,所以使用Session域监听器来判断session回话的关闭importjavax。servlet。ServletContext;importjavax。servlet。ServletContextEvent;importjavax。servlet。ServletContextListener;importjavax。servlet。annotation。WebListener;importjavax。servlet。http。;importjava。util。List;WebListener()publicclassMySessionListenerimplementsHttpSessionListener{privateServletContextsc;privateListStringips;privateHttpSessionsession;privateObjectclientIp;OverridepublicvoidsessionDestroyed(HttpSessionEventse){scse。getSession()。getServletContext();ips(ListString)sc。getAttribute(ips);sessionse。getSession();clientIpsession。getAttribute(clientIp);删除ip,如何获取IP,但是不可以从session获取到IP因为Session获取不到Request一个Session包含多个Request一个Request只对应一个Session所以获取不到,这时只能先从Request域中获取到的ips,放置到Session域然后从Session域中读取ips。remove(clientIp);session一失效就马上将此IP从链表中移除是错误的应该看此IP是否有另外的回话存在,如果有的话不能删除}}
此处代码是页面点击关闭后,激活的退出方法importjavax。servlet。ServletException;importjavax。servlet。annotation。WebServlet;importjavax。servlet。http。HttpServlet;importjavax。servlet。http。HttpServletRequest;importjavax。servlet。http。HttpServletResponse;importjavax。servlet。http。HttpSession;importjava。io。IOException;WebServlet(nameLogoutServlet,urlPatternslogoutServlet)publicclassLogoutServletextendsHttpServlet{privateHttpSessionsession;protectedvoiddoPost(HttpServletRequestrequest,HttpServletResponseresponse)throwsServletException,IOException{从域中获取一个session,设置为false如果域中存在一个session,则直接获取,如果不存在,则返回一个空的sessionsessionrequest。getSession(false);if(session!null){使session失效session。invalidate();失效后,需要进行的操作,List链表中需要减去,用到了Session域监听器}}protectedvoiddoGet(HttpServletRequestrequest,HttpServletResponseresponse)throwsServletException,IOException{doPost(request,response);}}
在jsp页面进行读取的时候,因为ips是以List链表的形式存在的,所以要想判断当前在线人数,所以必须要判断链表的长度,所以是applicationScope。ips。size()lt;pagecontentTypetexthtml;charsetUTF8languagejavahtmlheadtitleTitlelt;titleheadbodycenterh1Youarethe{applicationScope。ips。size()}customertovisit。h1
h3安全退出h3centerbodyhtml
好了?,这时候,程序写完了,如何判断呢?
此时,我们的程序是部署在本地的Tomcat上的,对于本台电脑,只有一个IP,如何实现多个IP呢?其实啊我们的电脑可以有三个IP,在访问服务器的时候,服务器的IP多写几个,相当于本机的IP多出来几个。是哪三个IP呢?
1、默认clientIp:0:0:0:0:0:0:0:1
2、127。0。0。1
这时大家可能会问127。0。0。1和localhost有什么区别呢,其实在这里要区分三个概念:
localhost、127。0。0。1和本机IP之间的区别:localhost等于127。0。0。1,不过localhost是域名,127。0。0。1是IP地址。localhost和127。0。0。1不需要联网,都是本机访问。本机IP需要联网,本机IP是本机或外部访问,本机IP就是本机对外放开访问的IP地址,这个网址就是与物理网卡绑定的IP地址。
3、IPv4地址:192。168。1。110
这样就很完美的实现了本地三个IP的测试。
写到这里,似乎已经可以简单的测试当前在线人数,也许仔细的人会发现在Session域被销毁的方法中的注释中发现一些猫腻。大家可以仔细想想,如果客户端用不同的浏览器,相同的IP去访问呢?点击退出后,会不会出现错误情况呢?答案是会的。演示结果如下图
最完美的代码
所以在点击退出登录的按钮之后,不可以直接将IP移除,要判断有没有另外的回话存在,如果有另外的回话存在,此IP是不可以删掉的,问题由此变的复杂了,因为还要统计此IP所发出的会话有多少。
整体思路:
在全局域中,将不是直接将iP存放在List的链表中,而是以一个Map的形式存在,Map的键为String类型,Key为List类型,List中存放的是当前IP所激发的会话对象,这样就可以统计,一个IP触发的sessions有多少个。
通过调用Map的get方法,将当前IP最为参数,将可以获取到他所激发的会话集合。但是,此集合可能为空,因为有可能当前IP一次也没有访问此页面,所以在List为空的时候好要创建一个ArrayList来存放sessions,然后将变化后的List重新写回到Map,再将变化后的Map写回到全局域中。这样创建过程基本完成。
然后考虑销毁过程,IP还需方法放到Session域中,当session被销毁的时候,应该把当前Session从List中删除,但是Map中此sessions对应的IP可是不能直接删,要判断List中的sessions的个数(Entry对象),个数为1的时候才可以删除,不然就不可以删除。
所以,要将当前IP通过Request域存放到当前Session域中,
然后,要考虑的问题是,每次刷新页面后sessions的个数会增加,这是错误的,原因是什么?
答案是,因为在存放sessions的时候,创建数组直接进行的添加,这样的话,每次一刷新页面,就会导致sessions的添加,所以在此之前应该判断,sessions中是否有此session,有的话直接跳出。
这样添加就没问题了Servlet域中添加Map
在Map中,需要使用键值对的方式,Key为IP,Value为List,那么List中存放什么呢?存放的是此IP发出的所有回话的HttpSession的对象,所以List的泛型是HttpSession。
请求,在请求中,因为将当前Session对象存放到List中,List在Map中,Map在全局域中,所以首先得从全局域获取到Map,然后,从Map中获取由当前IP所发出的所有Session的组成的List,判断当前的List是否为NULL,若为NULL,则创建List,否则,将当前SessioncurrentSession放入List中。importjavax。servlet。;importjavax。servlet。annotation。WebListener;importjavax。servlet。http。;importjava。util。ArrayList;importjava。util。List;importjava。util。Map;WebListener()publicclassMyRequestListenerimplementsServletRequestListener{privateHttpServletRequestsr;privateStringclientIp;privateServletContextsc;privateListStringips;privateHttpSessioncurrentSession;privateMapString,ListHttpSessionmap;privateListHttpSessionsessions;Override请求被初始化RequestpublicvoidrequestInitialized(ServletRequestEventsre){从请求域中获取IPsr(HttpServletRequest)sre。getServletRequest();clientIpsr。getRemoteAddr();currentSessionsr。getSession();将当前Session对象存放到List中,List在Map中,Map在全局域中,scsre。getServletContext();map(MapString,ListHttpSession)sc。getAttribute(map);从Map中获取由当前IP所发出的所有Session的组成的Listsessionsmap。get(clientIp);判断当前的List是否为NULL,若为NULL,则创建List,否则,将当前Session放入Listif(sessionsnull){sessionsnewArrayList();}遍历List的session对象,若有则不添加,若没有则添加for(HttpSessionsession:sessions){if(sessioncurrentSession)return;}sessions。add(currentSession);将变化过的List重新写回到Mapmap。put(clientIp,sessions);再将变化的Map写回到全局域中sc。setAttribute(map,map);将当前IP放入到当前SessioncurrentSession。setAttribute(clientIp,clientIp);}}ServletContext
这里将不使用ips了,所以将其删除importjavax。servlet。ServletContext;importjavax。servlet。ServletContextEvent;importjavax。servlet。ServletContextListener;importjavax。servlet。annotation。WebListener;importjavax。servlet。http。;importjava。util。ArrayList;importjava。util。HashMap;importjava。util。List;importjava。util。Map;WebListener()publicclassMyServletContextListenerimplementsServletContextListener{privateServletContextsc;OverrideApplication被初始化的时候创建publicvoidcontextInitialized(ServletContextEventsce){创建一个Map,key为IP,value为该IP上所发出的会话的对象MapString,ListHttpSessionmapnewHashMap();scsce。getServletContext();将map放到全局域中sc。setAttribute(map,map);}}Session监听器
接下来剖析Session的删除工作,获取当前Session对象,这里有之前传递过来的IP,在进行删除操作的时候,要注意此处,删除的是List中的sessions,删除之后,还要判断其IP的是否要删除,如果List中没有该元素,则说明当前IP所发出的会话全部关闭,就可以从map中将当前IP对应的Entry对象删除,否则,当前IP所发出的会话任存在,那么使用put方法将变化过的List写回到map。importjavax。servlet。ServletContext;importjavax。servlet。ServletContextEvent;importjavax。servlet。ServletContextListener;importjavax。servlet。annotation。WebListener;importjavax。servlet。http。;importjava。util。List;importjava。util。Map;WebListener()publicclassMySessionListenerimplementsHttpSessionListener{privateServletContextsc;privateListStringips;privateHttpSessioncurrentSession;privateStringclientIp;privateMapString,ListHttpSessionmap;privateListHttpSessionsessions;OverridepublicvoidsessionDestroyed(HttpSessionEventse){scse。getSession()。getServletContext();currentSessionse。getSession();clientIp(String)currentSession。getAttribute(clientIp);map(MapString,ListHttpSession)sc。getAttribute(map);从Map中获取Listsessionsmap。get(clientIp);从List中删除当前Session对象sessions。remove(currentSession);如果List中没有该元素,则说明当前IP所发出的会话全部关闭,就可以从map中将当前IP对应的Entry对象删除若List中仍有元素,当前IP所发出的会话任存在,那么将变化过的List写回到mapif(sessions。size()0){map。remove(clientIp);}else{map。put(clientIp,sessions);}sc。setAttribute(map,map);}}
因为处理的退出的页面logoutServlet不需要做任何不同的处理,所以这里将不再重复。
因为在jsp用到了JSP标准库,所以到导两个包。lt;pagecontentTypetexthtml;charsetUTF8languagejavalt;taglibprefixcurihttp:java。sun。comjspjstlcorehtmlheadtitleTitlelt;titleheadbodycenterh1Youarethe{applicationScope。map。size()}customertovisit。h1
h3安全退出
h3h2c:forEachitems{map}varentry{entry。key}{entry。value。size()}
c:forEachh2centerbodyhtml
最后测试成功,这就是一个完美的统计当前用户的在线人数。
来源:https:mp。weixin。qq。comsgMlyh9FeFKRRAJlPY83g
上海110月实现新能源汽车产量77万辆同比增长66金一丹中国证券报中证网中证网讯(记者金一丹)11月9日,由中国汽车工业协会主办的第12届中国汽车论坛在上海嘉定举办,上海市经济信息化委副主任汤文侃在大会论坛上表示,上海将……
智能手机,给中小学生带来了什么?手机,自从被发明以来,给我们的生活的各个方面带来了巨大的影响,最初是为了交流、沟通得更加便捷、及时,到今天,已经快速发展到智能手机并且以更快的速度更新换代,极大地方便了我们的生……
DIDOG30S智能手表,一款颜值在线,功能丰富的健康手表背景随着智能穿戴的快速发展,越来越多的人开始使用智能手环或智能手表,相对于传统机械石英手表来说,智能穿戴不仅价格方面要便宜很多,而且颜值更高,功能更丰富。比如我最近刚入手……
农民写在第五个中国农民丰收节之际(现代诗歌)【原创】文李孝贤中国农民丰收节时间,在燃烧照亮整个心灵的空间心波,已涟漪拥抱在希望的琴弦上共振颤抖急促地呼吸里演绎着生命的法则……
三星苹果华为手机三大巨头销量对比据说苹果手机销量最近这段时间并不是太理想,有些黄牛已经开始出现了倒贴钱出售了,真的是砸手里了。其中三星虽然在国内的销量不太理想,但是在全球的范围来看,仍然是稳坐头一把交椅,20……
日本经济恐将倒退30年?跌出世界前三?近日,有日媒报道称,以美元计价的日本国内生产总值(GDP)正在萎缩,今年可能低于4万亿美元(约560万亿日元)。这将是近30年来首次,这也将意味着目前位列世界第三的日本经济体量……
便宜好用的四神丸,不仅温肾散寒,涩肠止泻还能治疗这五种病药店里便宜又好用的四神丸,它的主要作用是温肾散寒,涩肠止泻,但是你知道吗,它还可以治疗这五种病,今天贾医生就来一次给你讲明白,建议大家点赞收藏!首先我们先来看一下它里面的……
阿千日本探险日记这次打算挑没吃过的店看了一轮选了两间新宿的店家试试看第一间是是主打小鱼干为汤底的煮干王煮干凪店门口太像通往地狱的路之类的犹豫超久才敢进去哈哈哈后来……
我在珠海淇澳岛这趟旅行我等了有大半年了。半年前我就想过来了,但每次放假的时候这条线路旅游团都不开,因为不满15人报名。这次总算是成团了,终于可以出来玩了。上午大巴到达珠海淇澳岛后,我们……
56岁温碧霞生图状态翻车,皮肤松弛皱纹布满全脸,强行装嫩遭吐近日,有网友晒出一段温碧霞出席时装周的生图近照,画面中的她满脸老态,与精修图判若两人,消息一出,迅速引发广泛讨论。温碧霞身穿吊带露肩短裤,一头卷发披肩,手上戴着几十万的手……
东盟十国,为何不跟着美国走?中方的一组数据,就是最好的答案近段时间以来,东盟与中方就《南海行为准则》进行了深入磋商,取得了重大进展,相关文本草案,已经进入第二轮审阅环节。很显然,东盟国家在一系列涉华决策,尤其是在南海问题上,并未跟着美……
疏通人体背部膀胱经,祛湿排毒的最大通道人体气血走膀胱经,膀胱经是人体最大的去湿排毒的通路,那么做好这条经络的气血调养就有着十分重要的意义。膀胱经行走于人体的背部,人体的五脏六腑均可在背部找到相应的背俞穴及反射……