A 聊天室模块
A.1 聊天室模块概述
A.1.1 功能描述
聊天室的基本功能就是聊天,其他功能都是对基本功能的一个强化。本章中的聊天室主要包括以下功能。
?用户登录,包括验证用户是否在线。
?实时显示在线人员列表及在线人数。
?用户发言,包括选择表情和字体颜色功能。
?安全退出聊天室。
A.1.2 系统流程
聊天室的系统流程如图A.1所示。
图A.1 系统流程图
A.1.3 主界面预览
为了使读者对本模块有一个基本的了解,下面将给出聊天室的主界面预览效果图。聊天室的主界面运行结果如图A.2所示。
1
2
图A.2 主界面运行结果图
A.2 关键技术
在模块中,应用的关键技术为Ajax 重构和Application 对象的相关方法,下面进行详细介绍。
A.2.1 Ajax 重构
随着Ajax 应用程序的不断扩展,将会有越来越多的JavaScript 代码应用到Ajax 中,这可能导致许多意想不到的问题。因此有必要对Ajax 代码进行重构。下面将介绍实现Ajax 重构的基本步骤。
创建一个单独的JS 文件,名称为AjaxRequest.js ,并且在该文件中编写重构Ajax 所需的代码,具体代码如下:
var net=new Object(); //编写构造函数
net.AjaxRequest=function(url,onload,onerror,method,params){ this.req=null;
this.onload=onload;
this.onerror=(onerror) ? onerror : this.defaultError; this.loadDate(url,method,params); }
//编写用于初始化XMLHttpRequest 对象并指定处理函数,最后发送HTTP 请求的方法 net.AjaxRequest.prototype.loadDate=function(url,method,params){ if (!method){
method="GET"; }
if (window.XMLHttpRequest){ this.req=new XMLHttpRequest(); } else if (window.ActiveXObject){
this.req=new ActiveXObject("Microsoft.XMLHTTP"); }
if (this.req){ try{
var loader=this;
this.req.onreadystatechange=function(){
net.AjaxRequest.onReadyState.call(loader);
}
this.req.open(method,url,true);
if(method=="POST"){
this.req.setRequestHeader("Content-Type","application/x-www-form-urlencoded");
}
this.req.send(params);
}catch (err){
this.onerror.call(this);
}
}
}
//重构回调函数
net.AjaxRequest.onReadyState=function(){
var req=this.req;
var ready=req.readyState;
if (ready==4){
if (req.status==200 ){
this.onload.call(this);
}else{
this.onerror.call(this);
}
}
}
//重构默认的错误处理函籹
net.AjaxRequest.prototype.defaultError=function(){
alert("错误数据\n\n回调状态:"+this.req.readyState+"\n状态: "+this.req.status);
}
在需要应用Ajax的页面中应用以下的语句包含JS文件“AjaxRequest.js”。
在应用Ajax的页面中编写错误处理的方法、实例化Ajax对象的方法和回调函数,具体代码如下:
/******************错误处理的方法*******************************/
function onerror(){
alert("您的操作有误!");
}
/******************实例化Ajax对象的方法*******************************/
function getCheckCode1(showCheckCode,checkCode){
var loader1=new net.AjaxRequest("getPictureCheckCode.jsp?nocache="+
new Date().getTime(),deal_getCheckCode,onerror,"GET");
showCheckCode.style.display='';
checkCode.focus();
}
/************************回调函数**************************************/
function deal_getCheckCode(){
document.getElementById("showCheckCode").innerHTML=this.req.responseText;
}
A.2.2 Application对象
Application对象是一个环境上下文(Context)对象,用于保存应用程序中的所有公有数据,服务器启动并且自动创建Application对象以后,只要没有关闭服务器,Application 对象将一直存在,所有用户可以共享Application对象。
Application对象为JSP提供的内置对象,可以直接使用,但是在Servlet中,需要通过以下代码来获取该对象的实例,才可以使用该对象。
ServletContext application = getServletContext();
3
Application 对象提供了很多方法,本实例中,只使用了setAttribute()方法和getAttribute()方法。
首先来介绍setAttribute()方法,该方法用于将一个对象绑定到一个属性上,该属性在整个Servlet 上下文中都适用。setAttribute()方法的语法格式如下:
application.setAttribute(String name,Object object);
?
name :用于指定一个属性名,该属性在整个Servlet 上下文中都适用。 ? object :用于指定属性值。
例如,在本实例中,设置保存聊天信息的Application 对象的属性的具体代码如下:
application.setAttribute("message", sourceMessage);
下面再来介绍getAttribute()方法,该方法用于返回Application 对象的指定属性的值。如果不存在该属性,则返回null 。getAttribute()方法的基本语法如下:
application. getAttribute(String name);
?
name :用于指定一个属性名。
例如,在本实例中,设置获取聊天信息的Application 对象的属性的具体代码如下:
String sourceMessage = application.getAttribute("message").toString();
A.3 聊天室的实现过程
A.3.1 实现用户登录
当用户访问聊天室时,首先进入的是登录聊天室页面。在该页面中,用户输入自己喜欢的用户名,如图A.3所示,单击“进入”按钮,系统将判断输入的用户是否被占用,如果被占用将给出提示,否则进入到聊天室主界面。
图A.3 用户登录页面
实现用户登录的具体步骤如下。
(1)编写聊天室登录页面index.jsp ,在该页面中添加如表A.1所示的表单及表单元素。
表A.1 index.jsp 页面所涉及的表单及表单元素
(2)编写用来保存在线用户和对在线用户进行具体操作的类,这里为UserInfo,在该类中主要包括返回外界使用的实例对象、添加用户、获取用户列表和移用户的方法。UserInfo类的具体代码如下:
package com.wgh.model;
import java.util.Vector;
public class UserInfo {
private static UserInfo user = new UserInfo();
private Vector vector = null; // 利用private调用构造函数,防止被外界产生新的instance对象
public UserInfo() {
this.vector = new Vector();
}
// 外界使用的instance对象
public static UserInfo getInstance() {
return user;
}
// 增加用户
public boolean addUser(String user) {
if (user != null) {
this.vector.add(user);
return true;
} else {
return false;
}
}
// 获取用户列表
public Vector getList() {
return vector;
}
// 移除用户
public void removeUser(String user) {
if (user != null) {
vector.removeElement(user);
}
}
}
(3)创建UserListener类,主要实现valueBound(HttpSessionBindingEvent arg0)和valueUnbound(HttpSessionBindingEvent arg0)方法,用于监控session中的对象变化情况。在该方法中,在valueBound()方法中向控制台输出上线用户的信息,在valueUnbound()方法中向控制台输出下线用户的信息。UserListener类的具体代码如下:
package com.wgh.servlet;
import https://www.doczj.com/doc/704570899.html,erInfo;
import javax.servlet.http.HttpSessionBindingEvent;
public class UserListener implements
javax.servlet.http.HttpSessionBindingListener {
private String user;
private UserInfo container = UserInfo.getInstance(); //获得UserInfo类的对象
public UserListener() {
user = "";
}
// 设置在线监听人员
public void setUser(String user) {
https://www.doczj.com/doc/704570899.html,er = user;
}
// 获取在线监听
5
6
public String getUser() {
return https://www.doczj.com/doc/704570899.html,er;
}
// 当Session有对象加入时执行的方法
public void valueBound(HttpSessionBindingEvent arg0) {
System.out.println("上线用户:" + https://www.doczj.com/doc/704570899.html,er);
}
// 当Session有对象移除时执行的方法
public void valueUnbound(HttpSessionBindingEvent arg0) {
System.out.println("下线用户:" + https://www.doczj.com/doc/704570899.html,er);
if (user != "") {
container.removeUser(user);
}
}
}
说明:HttpBindingListener接口的valueBound()方法在有对象加入session时会被自动
执行,而valueUnbound()方法则是有对象从session中移除时会被自动执行。
(4)编写聊天室的Servlet实现类,并在该类中加添加登录聊天室的方法loginRoom(),在该方法中,首先获取登录用户,然后判断该用户是否登录,如果已经登录,给出提示信息,否则将该用户添加到在线用户列表中,并且向Application对象的聊天内容属性中添加一条系统公告信息,最后,将页面重定向到登录成功页。loginRoom()方法的具体代码如下:public void loginRoom(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
response.setContentType("text/html;charset=UTF-8");
HttpSession session = request.getSession();
String username=request.getParameter("username"); //获得登录用户名
UserInfo user=UserInfo.getInstance(); //获得UserInfo类的对象
session.setMaxInactiveInterval(600); //设置Session的过期时间为10分钟
Vector vector=user.getList();
boolean flag=true; //标记是否登录的变量
//判断用户是否登录
if(vector!=null&&vector.size()>0){
for(int i=0;i if(username.equals(vector.elementAt(i))){ PrintWriter out; try { out = response.getWriter(); out.println(""); } catch (IOException e) { e.printStackTrace(); } flag=false; break; //跳出for循环 } } } //保存用户信息 if(flag){ UserListener ul=new UserListener(); //创建UserListener的对象 ul.setUser(username); //添加用户 user.addUser(ul.getUser()); //添加用户到UserInfo类的对象中 session.setAttribute("user",ul); //将UserListener对象绑定到Session中 session.setAttribute("username",username); //保存当前登录的用户名 session.setAttribute("loginTime",new Date().toLocaleString()); //保存登录时间 ServletContext application=getServletContext(); //获取Application对象 String sourceMessage=""; if(null!=application.getAttribute("message")){ sourceMessage=application.getAttribute("message").toString(); 7 } sourceMessage+="系统公告:" + username + "走进了聊天室! try { request.getRequestDispatcher("login_ok.jsp").forward(request, response); } catch (Exception ex) { Logger.getLogger(Messages.class.getName()).log(Level.SEVERE, null, ex); } } } (5)编写登录成功页login_ok.jsp ,在该文件中,将页面重定向到聊天室的主界面。登录成功页的具体代码如下: <%@page contentType="text/html" pageEncoding="UTF-8"%> <%@taglib prefix="c" uri="https://www.doczj.com/doc/704570899.html,/jsp/jstl/core"%> A.3.2 编写聊天室的主界面 在用户登录页面中,输入用户名,单击“进入”按钮,即可进入到聊天室的主界面,如图A.2所示,在该页面中共包括页面头部、在线人员列表区、聊天内容显示区和用户发言区4部分,具体布局如图A.4所示。 图A.4 主页面的布局图 聊天室主界面主要用采用 … … 8 A.3.3 实时显示在线人员列表 在开发聊天室程序时,为了让用户及时了解在线线用户,并与各用户进行交流,需要提供实时获取并显示在线人员列表的功能。在本章中介绍的聊天室的主界面中,左侧显示在线人员列表,及在线人数,如图A.5所示。 图A.5 在线人员列表 在实现在线人员列表显示时,为了实时显示在线人员列表,这里应用了Ajax 技术,关于Ajax 重构的方法,请参见A.2.1节。实现实时显示在线人员列表的具体步骤如下。 (1)在main.jsp 页面中,编写自定义的JavaScript 函数showOnline(),用于实例化Ajax 对象。showOnline()函数的具体代码如下: function showOnline(){ var loader=new net.AjaxRequest("online.jsp?nocache="+ new Date().getTime(),deal_online,onerror,"GET"); } 在上面的代码中,一定要加代码“?nocache="+new Date().getTime()”,否则将出现在线人员列表不更新的情况。 (2)创建online.jsp 文件。在该文件中,主要是将保存到集合类中的在线人员列表显示到页面。online.jsp 页面的代码如下: <%@page contentType="text/html" pageEncoding="UTF-8" %> <%@ page import="https://www.doczj.com/doc/704570899.html,erInfo"%> <%@ page import="java.util.*"%> <% UserInfo list=UserInfo.getInstance(); Vector vector=list.getList(); int amount=0; %> <%if(vector!=null&&vector.size()>0){ String username=""; amount=vector.size(); for(int i=0;i 9 (3)在聊天室的主界面中,将左侧用于显示在线人员列表的单元格的id 属性设置为online ,用于实时显示在线人员列表,具体代码如下: (4)编写Ajax 的回调函数deal_online(),用于将获取的在线人员列表赋值给id 为online 的 function deal_online(){ online.innerHTML=this.req.responseText; } (5)为了让页面载入后,就调用Ajax 获取在线人员列表,并且每隔10秒钟,便获取一次数据,还需要在页面中添加以下JavaScript 代码。 window.setInterval("showOnline();",10000); window.onload=function(){ showOnline(); //当页面载入后显示在线人员列表 } (6)细心的读者可以会发现,聊天对象文本框被设置为只读属性,这样用户就不能手动输入聊天对象,所以还需要提供选择聊天对象的功能,这可以通过在主页面中添加选择聊天对象的JavaScript 自定义函数及在在线人员列表上添加超链接实现。实现将选择的聊天对象添加到聊天对象文本框的JavaScript 代码如下: (7)在在线人员列表上添加超链接,具体代码如下: A.3.3 实现用户发言 在聊天室程序中,一个必不可少的功能就是实现用户发言,在本章介绍的聊天室中,用户发言区位于聊天室主界面的底部,如图A.6所示。 图A.6 用户发言区 实现用户发言的具体步骤如下。 (1)在页面的合适位置添加用于收集用户发言信息的表单及表单元素,如表A.2所示。 表A.2 用户发言区所涉及的表单及表单元素 10 首先验证用户输入信息的合法性,然后再将提交的表单元素的内容连接为一个参数字符串,并实例化Ajax 对象。send()函数的具体代码如下: function send(){ //验证聊天信息并发送 if(form1.to.value==""){ alert("请选择聊天对象!");return false; } if(form1.content1.value==""){ alert("发送信息不可以为空!");form1.content1.focus();return false; } var param="from="+form1.from.value+"&face="+form1.face.value+"&color="+form1.color.value+ "&to="+form1.to.value+"&content="+ form1.content1.value; var loader=new net.AjaxRequest("Messages?action=sendMessage",deal_send,onerror,"POST",param); } (3)在聊天室相关的Servlet 实现类中,添加发送聊天信息的方法sendMessages()。在该方法中,首先获取用户发言相关信息,并对可能出现中文的信息进行转码,然后判断保存当天聊天信息的XML 文件是否存存,如果不存在,则创建该XML 文件,最后将聊天信息保存到XML 文件中,并重定向网页。sendMessages()方法的具体代码如下: public void sendMessages(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { response.setContentType("text/html;charset=UTF-8"); request.setCharacterEncoding("UTF-8"); Random random = new Random(); String from = request.getParameter("from"); //发言人 String face = request.getParameter("face"); //表情 String to = request.getParameter("to"); //接收者 String color = request.getParameter("color"); //字体颜色 String content = request.getParameter("content"); //发言内容 String sendTime = new Date().toLocaleString(); //发言时间 ServletContext application = getServletContext(); String sourceMessage = application.getAttribute("message").toString(); try { //发言时间 sourceMessage += "" + from + "" + face + "对[" + to + "]说:" + "" + content + "(" + sendTime + ") application.setAttribute("message", sourceMessage); 11 request.getRequestDispatcher("Messages?action=getMessages&nocache=" + random.nextInt(10000)).forward(request, response); } catch (Exception ex) { Logger.getLogger(Messages.class.getName()).log(Level.SEVERE, null, ex); } } A.3.4 实时显示聊天内容 在聊天室程序中,另一个必不可少的功能就是实现显示聊天内容。这里介绍的聊天室中,聊天内容显示区位于聊天室主界面的右侧,如图A.7所示。 图A.7 聊天内容显示区 在实现显示聊天内容时,为了实时显示聊天内容,这里应用了Ajax 技术,关于Ajax 重构的方法,请参见A.2.1节。实现实时显示聊天内容的具体步骤如下。 (1)编写自定义的JavaScript 函数showContent(),用于实例化Ajax 对象。 showContent()函数的具体代码如下: function showContent(){ var loader1=new net.AjaxRequest("Messages?action=getMessages&nocache="+ new Date().getTime(),deal_content,onerror,"GET"); } (2)在聊天室相关的Servlet 实现类中,编写getMessages()方法,用于将页面重定向到显示聊天内容的JSP 页面。getMessages()方法的具体代码如下: public void getMessages(HttpServletRequest request,HttpServletResponse response) { response.setContentType("text/html;charset=UTF-8"); try { request.getRequestDispatcher("content.jsp").forward(request, response); } catch (Exception ex) { Logger.getLogger(Messages.class.getName()).log(Level.SEVERE, null, ex); } } (3)编写显示聊天内容的JSP 页面,在该页面中只需要应用EL 表达式将返回的执行结果输出即可,具体代码如下: <%@page contentType="text/html" pageEncoding="UTF-8" %> ${message} (4)在聊天室的主界面中,在右侧用于显示聊天内容的单元格,添加一个id 属性为content 的 12 (5)编写Ajax 的回调函数deal_content(),在该函数中,首先获取Ajax 处理页的返回值,然后去除字中的Unicode 空白符,最后判断在获取信息时是否产生错误,如果是则退出聊天室,否则,将获取的聊天内容赋值给id 为content 的 function deal_content(){ var returnV alue=this.req.responseText; //获取Ajax 处理页的返回值 var h=returnV alue.replace(/\s/g,""); //去除字符串中的Unicode 空白符 if(h=="error"){ Exit(); }else{ content.innerHTML=sysBBS+returnValue+""; //当聊天信息超过一屏时,设置最先发送的聊天信息不显示 document.getElementById('content').scrollTop = document.getElementById('content').scrollHeight*2; } } (6)为了让页面页面载入后,就调用Ajax 获取聊天内容,并且每隔1秒钟,便获取一次数据,还需要在页面中添加以下JavaScript 代码。 window.setInterval("showContent();",1000); window.onload=function(){ showContent(); //当页面载入后显示聊天内容 } A.3.5 退出聊天室 在聊天室的主界面中,单击“退出聊天室”按钮,要以退出聊天室。实现退出聊天室的具体步骤如下: (1)编写自定义的JavaScript 函数Exit(),在该函数中首先将页面重定向到退出聊天室页面leave.jsp ,然后再弹出“欢迎您下次光临!”对话框,具体代码如下: function Exit(){ window.location.href="leave.jsp"; alert("欢迎您下次光临!"); } (2)在“退出聊天室”按钮的onclick 事件中调用自定义的JavaScript 函数Exit(),关键代码如下: (3)编写退出聊天室页面leave.jsp ,在该页面中,首先销毁Session ,然后将页面重定向到登录页面。leave.jsp 页面完成代码如下: <%@page contentType="text/html" pageEncoding="UTF-8" %> <% session.invalidate(); response.sendRedirect("index.jsp"); %>
"; application.setAttribute("message",sourceMessage);标记进行部局,其关键代码如下:
在线人员列表 欢迎来到心之语聊天室! 所有人 <%}}%> <%=username%> 当前在线[<%=amount%>]人 在线人员列表 标记的innerHTML 属性。deal_online()函数的具体代码如下:
";