在JSP或Servlet中獲取session數(shù)量
很多網(wǎng)友都問(wèn)如何制作社區(qū)的在線人員顯示問(wèn)題,這也是個(gè)老大難的問(wèn)題。由于筆者已經(jīng)學(xué)習(xí)和實(shí)踐了這些方面的編程已經(jīng)有很長(zhǎng)一段時(shí)間了。不說(shuō)是有技術(shù),也算是有些經(jīng)驗(yàn)了,在這些問(wèn)題解決方面我也曾遇到比較多的問(wèn)題,后來(lái)總是解決了。 退出登錄"); 【編輯推薦】
這次和大家講的不關(guān)是一個(gè)程序的代碼實(shí)現(xiàn),而更重要的就是一個(gè)原理和思想問(wèn)題,因?yàn)槿绾未蠹叶贾肋@個(gè)該如何去做的話,就不存在技術(shù)上的難題了,而只是一個(gè)語(yǔ)法上的不熟罷了。
其實(shí)判斷用戶是否登錄很簡(jiǎn)單,只要在登錄的程序加上 "把用戶設(shè)為在線" 就行了,這可以用SQL的update來(lái)做也可以用其他的如對(duì)文本文件的追加來(lái)做。如何登錄時(shí)是設(shè)置的可以COOIKE登錄的話,你只要在瀏覽頁(yè)面或是發(fā)表貼子等的頁(yè)面,進(jìn)行更新也可以。
其實(shí)網(wǎng)友認(rèn)為難的就在于如何知道用戶已經(jīng)離開(kāi)社區(qū)了。下面有幾個(gè)初學(xué)網(wǎng)友會(huì)犯的錯(cuò)誤。
錯(cuò)誤方法一: 當(dāng)然有的網(wǎng)友會(huì)做在社區(qū)里做個(gè) "退出社區(qū)" 可有的用戶比較忙或是手比較快,就會(huì)直接的關(guān)閉瀏覽器了。那么在程序看來(lái)此用戶根本就沒(méi)有退出社區(qū)。
錯(cuò)誤方法二: 為了這樣也有的網(wǎng)友以關(guān)閉瀏覽器為標(biāo)準(zhǔn),例如每當(dāng)用戶直接關(guān)閉瀏覽頁(yè)面時(shí),用JavaScript執(zhí)行一個(gè)unload事件,跳出一個(gè)窗口,來(lái)執(zhí)行相就在的更新,這也許是解決直接關(guān)閉的問(wèn)題??墒蔷W(wǎng)友沒(méi)有考慮到如果因?yàn)橘N子或文章太長(zhǎng),用戶想斷了線后,再好好的看看,那么他正好是直接斷線后,看完所有貼子或文章再關(guān)閉瀏覽器的呢。此時(shí)跳出窗口頁(yè)面的程序就等于沒(méi)有執(zhí)行。
錯(cuò)誤方法三:使用session為例,一般的服務(wù)器中都有Session的失效時(shí)間,如IIS中為20分鐘,有的網(wǎng)友以Session為標(biāo)準(zhǔn),來(lái)判斷,這顯然也是行不通的,結(jié)果會(huì)和錯(cuò)誤方法二一樣。
那么如果才是正確的方法呢。到現(xiàn)在為此還沒(méi)有一種方便的和精確的方法,除非你用Job事務(wù)來(lái)做,當(dāng)那幾乎是不可能的,因?yàn)槊赓M(fèi)的,即使是收費(fèi)的主頁(yè)空間也不會(huì)讓你這么做的。所以只能能過(guò)普通的命令來(lái)實(shí)現(xiàn)了。
不過(guò)它的局限性就在于不能精確,但是我們可以讓他更加的接近于精確。
下面我們會(huì)以IIS環(huán)境下的SQL SERVER服務(wù)器為例子。其他的如PHP+MYSQL,JSP+MS SQL SERVER等舉一反三,幾乎語(yǔ)句都不用怎么變。
例子中采用程序片段,例如用戶在表users中,除了基本要有的用戶名字段(name)等,要有online字段,可以用bit類型就可以了。還要有一個(gè)登錄時(shí)間的字段logintime,和***操作時(shí)間的字段logouttime,都為datetime類型。詳細(xì)的作用我們會(huì)在后面談到。
首先在用戶登錄進(jìn)入時(shí),要生成一個(gè)Session("name")字段的值,或是cookies("club")("name")的值,以便于在進(jìn)行操作時(shí),讓SQL在社區(qū)中任意頁(yè)面都知道是哪個(gè)用戶的記錄。接著更新用戶的下online字段和logintime字段,語(yǔ)句如下:
conn.execute("Update users set online=1,logintime='"&now&"' where name='"&session("name")&"'")
這條語(yǔ)句的作用在于當(dāng)用戶登錄時(shí),就把用戶在線設(shè)為真,再把當(dāng)前操作時(shí)間設(shè)為現(xiàn)在。其次在用戶進(jìn)行貼子或文章瀏覽時(shí),也要用此語(yǔ)句,因?yàn)檫@樣才可能更加的準(zhǔn)確些。
在這些語(yǔ)句的后面再加上一句。
conn.execute("Update users set online=0 where diffdate(""n"",logintime,'"&now()&"')>10")
這條語(yǔ)句的作用表示把用戶***操作時(shí)間和當(dāng)前時(shí)間相比較,如果大于10分鐘的話,就會(huì)把這些用戶都設(shè)為離線。
好了,其實(shí)關(guān)鍵的也就是這兩條語(yǔ)句而以,是不是很簡(jiǎn)單?說(shuō)了這么多,其實(shí)想讓大家了解的就是實(shí)現(xiàn)的原理,而不是什么代碼。相信大家現(xiàn)在也可以很簡(jiǎn)單的做出關(guān)于PHP+MYSQL和JSP+數(shù)據(jù)庫(kù)的在線人員顯示的程序來(lái)了。
***可以用"select name from users where online=1"來(lái)調(diào)用在線的用戶了,然后顯示出來(lái)當(dāng)然也沒(méi)問(wèn)題了。呵。。。
***************************
namespace OnlineCount
{
using System;
using System.Data;
using System.Drawing;
using System.Web;
using System.Web.UI.WebControls;
using System.Web.UI.HtmlControls;
///
///Online 的摘要說(shuō)明。
///
public class Online : System.Web.UI.UserControl
{
protected System.Web.UI.WebControls.Label Label1;
private void Page_Load(object sender, System.EventArgs e)
{
if (Application["online"] == null)
{
DataTable dtnew = new DataTable();
dtnew.Columns.Add("userid",typeof(string));
dtnew.Columns.Add("lastaccesstime",typeof(DateTime));
Application["online"] = dtnew;
}
//DataTable dt = (DataTable)Application["online"];
DataTable dt = Application["online"] as DataTable;
DataRow[] rows = dt.Select("userid='"+Session.SessionID+"'");
if (rows.Length == 1)
{
DataRow dr = rows[0];
dr["lastaccesstime"] = DateTime.Now;
}
else
{
DataRow dr = dt.NewRow();
dr["userid"] = Session.SessionID;
dr["lastaccesstime"] = DateTime.Now;
dt.Rows.Add(dr);
}
for(int i = 0 ; i < dt.Rows.Count; i++)
{
DataRow dr = dt.Rows[i];
TimeSpan t = DateTime.Now - Convert.ToDateTime(dr["lastaccesstime"]);
if (t.Seconds > 8)
{
dr.Delete();
}
}
dt.AcceptChanges();
this.Label1.Text = dt.Rows.Count.ToString();
}
#region Web 窗體設(shè)計(jì)器生成的代碼
override protected void OnInit(EventArgs e)
{
//
// CODEGEN: 該調(diào)用是 ASP.NET Web 窗體設(shè)計(jì)器所必需的。
//
InitializeComponent();
base.OnInit(e);
}
///
///設(shè)計(jì)器支持所需的方法 - 不要使用代碼編輯器
///修改此方法的內(nèi)容。
///
private void InitializeComponent()
{
this.Load += new System.EventHandler(this.Page_Load);
}
#endregion
}
}
*********** OnlineUser.java ************
package org.sunxin.lesson.jsp.ch09.online;
import javax.servlet.*;
import java.io.*;
import javax.servlet.http.*;
import java.util.Enumeration;
public class OnlineUser extends HttpServlet
{
public void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException,IOException
{
req.setCharacterEncoding("gb2312");
String name=req.getParameter("user");
String pwd=req.getParameter("password");
if(null==name || null==pwd || name.equals("") || pwd.equals(""))
{
resp.sendRedirect("login.html");
}
else
{
HttpSession session=req.getSession();
User user=(User)session.getAttribute("user");
if(null==user || !name.equals(user.getName()))
{
user=new User(name);
session.setAttribute("user",user);
}
resp.setContentType("text/html;charset=gb2312");
PrintWriter out=resp.getWriter();
out.println("歡迎用戶"+name+"登錄");
UserList ul=UserList.getInstance();
out.println("
當(dāng)前在線的用戶列表:
");
Enumeration
int i=0;
while(enums.hasMoreElements())
{
out.println(enums.nextElement());
out.println(" ");
if(++i==10)
{
out.println("
");
}
}
out.println("
當(dāng)前在線的用戶數(shù):"+i);
out.println("
out.close();
}
}
public void doPost(HttpServletRequest req, HttpServletResponse resp)
throws ServletException,IOException
{
doGet(req,resp);
}
}
*********** LogoutServlet.java ************
package org.sunxin.lesson.jsp.ch09.online;
import javax.servlet.*;
import java.io.*;
import javax.servlet.http.*;
public class LogoutServlet extends HttpServlet
{
public void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException,IOException
{
resp.setContentType("text/html;charset=gb2312");
HttpSession session=req.getSession();
User user=(User)session.getAttribute("user");
session.invalidate();
PrintWriter out=resp.getWriter();
out.println("");
out.println(user.getName()+",你已退出登錄
");
out.println("重新登錄");
out.println("");
out.close();
}
}
*********** User.java ************
package org.sunxin.lesson.jsp.ch09.online;
import javax.servlet.http.HttpSessionBindingListener;
import javax.servlet.http.HttpSessionBindingEvent;
public class User implements HttpSessionBindingListener
{
private String name;
private UserList ul=UserList.getInstance();
public User()
{
}
public User(String name)
{
this.name=name;
}
public void setName(String name)
{
this.name=name;
}
public String getName()
{
return name;
}
public void valueBound(HttpSessionBindingEvent event)
{
ul.addUser(name);
}
public void valueUnbound(HttpSessionBindingEvent event)
{
ul.removeUser(name);
}
}
*********** UserList.java ************
package org.sunxin.lesson.jsp.ch09.online;
import java.util.Vector;
import java.util.Enumeration;
public class UserList
{
private static final UserList userList=new UserList();
private Vector
private UserList()
{
v=new Vector
}
public static UserList getInstance()
{
return userList;
}
public void addUser(String name)
{
if(name!=null)
v.addElement(name);
}
public void removeUser(String name)
{
if(name!=null)
v.remove(name);
}
public Enumeration
{
return v.elements();
}
public int getUserCount()
{
return v.size();
}
}
****************************
Java代碼的思路是將所有登陸用戶放到集合中。
用戶登陸成功就添加到用戶列表,用戶退出就從用戶列表中移出。
lz 注意User類所實(shí)現(xiàn)的接口以及其中以下兩個(gè)方法:
//對(duì)象被綁定到session中時(shí)通知該對(duì)象
public void valueBound(HttpSessionBindingEvent event)
{
ul.addUser(name);
}
//從session中刪除對(duì)象時(shí)通知該對(duì)象
public void valueUnbound(HttpSessionBindingEvent event)
{
ul.removeUser(name);
}