`

AJAX无刷新聊天室实现(压缩包附件下载)

阅读更多
之前也写了一个小的聊天室DEMO,在另外一篇博客中
http://lvp.iteye.com/blog/343236,也提供了附件下载。
但是这个示例中页面自动刷新 带来的体验感不好,且当聊天内容到达最后一行时,滚动条的滚动是用了非常耍赖的方法来解决的,因此,在此基础之上,做了一些改进。

我觉得,写代码,不见得一定要写很多的DEMO,但是力争每一个DEMO都永远比上一个好。有可能一直在写同一个示例,但是每一次的实现一定在改进,在推敲。所以我也一直在做这么一件事情,尽可能把每个小DEMO写好,永远比上一次要好,永远比上一次要碰到更多的问题,永远比上一次要多懂得一些解决这些问题的方法,我觉得这是初学者应该要懂得的一个道理。不应该过于贪多,认真的把一个问题给解决,分析与总结。

所以,对于我来说,这个示例只是比上一个示例要好了一些,但是也不是终点。

并且,为了对下载的朋友负责的态度,凡是提供下载的代码一定都是经过自己手动实现,并非抄袭他人,代码是齐全的,有数据库的就一定提供数据库,所以这些代码可以帮助你们解决一些具有同类功能的问题。

代码描述部分,红色字体是我碰到的问题,后来都解决了,大家可以查看是否遇到过。

这次的DEMO,主要解决了以下这些问题,或者说实现了以下的功能。

1.窗体的控制,只显示合适的窗体大小。
2.用户名从已经登陆用户中检查,是否已经存在同名的情况.ajax后台调用查找.
3.登录后主窗体显示,登陆窗体关闭
4.显示已经登录者,在线人数,可以发表信息
5.信息显示,发表消息,上线与下线,都感觉不到页面的刷新,ajax实现
6.滚动条的控制,有时需要看上面的消息,有时需要自动滚动,由你控制
7.点击退出时,关闭页面,其它窗体显示下线信息.


DEMO 的 ajax实现靠手工代码形式完成,页面布局和窗体定位也都是通过手写代码 css+div完成。


列出图片,说出主要功能实现以及流程
1.登陆页面,自动验证登陆名称是否被占用


2.登陆之后,登陆界面消失,聊天主界面消失掉。在线用户应该显示为 1人的,因为这是之前截取的,所以就没有修改。


3.再使用Simon lv这个名称就不行了,因为之前已经有这个用户。


4.换一个名字,就能登陆,登陆后的效果


5.多弄几个用户登陆进入


6.无刷新显示消息


7.张三退出后,聊天室信息显示离开,且在线人数马上发生改变


以上为图片部分,代码部分和流程部分看下面内容。

请求的首页为index.jsp
立马转向到 login.jsp,这时将自身关闭,打开 login.jsp页面时,改变打开的大小.
<%@ page language="java" pageEncoding="UTF-8" contentType="text/html; charset=UTF-8"%>
<html>
	<head>
		<script type="text/javascript">
			function openlogin(){
				window.open("login.jsp","","width=600px,height=460px");
				window.opener=null;
				this.close();
			}
		</script>
	</head>
	<body onload="openlogin()">
	</body>
</html>


中间的  window.opener=null; 不加的话,将会出现一个问题,就是在关闭 index.jsp自身时,会跳出一个提示框,提示是否需要关闭本页面,所以加上这句话就可以解决这个问题.


login.jsp中
检查用户是否存在部分
<td >
							用户名:
						</td>
						<td >
							<input type="text" class="normalTxt" name="name" onblur="checkUserIsExits()">
						</td>


checkUserIsExits 函数异步请求另外的一个页面

login.jsp
<%@ page language="java" pageEncoding="UTF-8" contentType="text/html; charset=UTF-8"%>
<html>
	<head>
		<title>聊天室登录</title>
		<link rel="stylesheet" type="text/css" href="css/styles.css">
		<script language="javascript">
			function showMess(mess){
				document.getElementById("mess").innerHTML="<font color='red'>"+mess+"</font>";
			}
			//检查用户名是否登录
			function checkUserIsExits(){
				var objName = document.myform.name;
				
				if(objName.value==""){
					objName.focus();
					showMess("请输入用户名!");
					return;
				}else{
					for(var i=0;i<objName.value.length;i++){
						var ch = objName.value.charAt(i);
						if(ch=='<' || ch =='>' || ch=='-' || ch=='/' || ch=='\\'){
							objName.focus();
							showMess("不能含有<,>,-等特殊字符!");
							return;
						}
					}
				}
				var url = "checkUser.jsp?user="+objName.value;
				sendRequest(url);
			}
			
			//定义XMLHttpRequest对象
			var xmlRequest;
			//创建对象
			function createXMLHttpRequest(){
				if(window.XMLHttpRequest) { //Mozilla 浏览器
					xmlRequest = new XMLHttpRequest();
				}else if (window.ActiveXObject) { // IE浏览器
					try {
						xmlRequest = new ActiveXObject("Msxml2.XMLHTTP");
					} catch (e) {
						try {
							xmlRequest = new ActiveXObject("Microsoft.XMLHTTP");
						} catch (e) {}
					}
				}
			}
			
			//发送请求函数
			function sendRequest(url){
				createXMLHttpRequest(); //创建对象
				url=encodeURI(url); 
				url=encodeURI(url); 
				xmlRequest.open("GET",url,true);
				xmlRequest.onreadystatechange = checkResponse; //响应的函数
				xmlRequest.send(null);
			}
			
			//响应的函数
			function checkResponse(){
				if(xmlRequest.readyState==4){
					if(xmlRequest.status ==200) {
						//信息已经成功返回
						showloginInfo();
					}else{
						showMess("您所请求的页面有异常!");
					}
				}
			}
			
			//显示函数
			function showloginInfo(){
				//取得 checkUser.jsp返回的XML文本  
				var message = xmlRequest.responseXML.getElementsByTagName("userinfo")[0].firstChild.nodeValue;
				showMess(message);
				//因为之前解析XML 一直出现错误 所以改用了XML文本  先显示 后取出来
				if(message=="可以使用"){
					showMess("<font color='blue'>可以使用该名称!</font>");
					document.getElementById("sub").disabled=false; //使按钮可用
				}
			}
		</script>
	</head>
	<body>
		<div id="mainDiv">
			<form action="dologin.jsp" name="myform" method="post">
				<table align="left" width="300">
					<caption align="left"><font color="blue" size="6" face="隶书">o 蝈蝈岛聊天室 O</font></caption>
					<tr >
						<td >
							用户名:
						</td>
						<td >
							<input type="text" class="normalTxt" name="name" onblur="checkUserIsExits()">
						</td>
					</tr>
					
					<tr>
						<td colspan="2">
							<input type="submit" id="sub" class="normalBtn" value="登  录" disabled="true">
							&nbsp;
							<input type="reset" class="normalBtn" value="重  置">
							&nbsp;
						</td>
					</tr>
					<tr>
						<td colspan="2" align="right">
							<label id="mess">&nbsp;</label>
						</td>
					</tr>
					<tr>
						<td colspan="2">
						<br>
						<br>
						<br>
						Ajax 无刷新 简单多人聊天室DEMO,可到 <a href="http://lvp.iteye.com" target="_blank">http://lvp.iteye.com</a>下载
						&nbsp;&nbsp;&nbsp;	<a href="javascript:close()">退出</a></td>
					</tr>
				</table>
			</form>
		</div>
	</body>
</html>




注意,在login.jsp里面有一个函数
//发送请求函数
			function sendRequest(url){
				createXMLHttpRequest(); //创建对象
				url=encodeURI(url); 
				url=encodeURI(url); 
				xmlRequest.open("GET",url,true);
				xmlRequest.onreadystatechange = checkResponse; //响应的函数
				xmlRequest.send(null);
			}


出现两次,为什么一定要加上两次,我也没有深究,在传递中文参数之前这样编码一下,
url=encodeURI(url);
url=encodeURI(url);
然后在请求到的处理页面做一些解码动作
name = URLDecoder.decode(name,"UTF-8");
中文问题就不存在,所以也没有为什么,只是这样做了,问题就解决了。
并且一定要注意,在ajax里操作的字符编码格式都是 UTF-8,所以统一起来就没有什么问题.

与login.jsp紧密相关的checkUser.jsp页面
<%@ page language="java" pageEncoding="UTF-8"%>
<%@page import="java.util.ArrayList"%>
<%@page import="java.net.URLDecoder"%>
<%
	//获得参数 用户名
	String name = request.getParameter("user");
	//name = new String(name.getBytes("ISO-8859-1"),"UTF-8");
	name = URLDecoder.decode(name,"UTF-8");
	//System.out.print("检查用户输入的是:"+name);
	String responseText = "可以使用"; //响应字符串 
	//从application中取出所有已登录者的信息
	ArrayList<String> list = (ArrayList<String>)application.getAttribute("users");
	
	if(list!=null){
		for(String string:list){
			if(string.equals(name)){
				responseText = "这个名称已经被占用!";
				break;
			}
		}
	}
	
	//设置输出信息的格式及字符集 
	response.setContentType("text/xml; charset=UTF-8");
	response.setHeader("Cache-Control", "no-cache");
	out.print("<response>");
	out.print("<userinfo>");
	out.print(responseText);
	out.print("</userinfo>");
	out.print("</response>");
%>



流程:index.jsp 转到 login.jsp
login.jsp 中 
checkUser.jsp是用来查看用户是否存在的。
dologin.jsp 是用来处理登陆的。

dologin.jsp 有这几部分逻辑:
1.得到用户,放入到 session中
2.放入到 application中
ArrayList<String> messageList = (ArrayList<String>)application.getAttribute("messages");
	
		if(messageList==null){
			messageList = new ArrayList<String>();
		}
		
		messageList.add("\n欢迎,欢迎,大家热列欢迎!"+name+" , 进入聊天室了.....\n");
		application.setAttribute("messages",messageList);


3.转向
<script language="javascript">
			//显示新窗口
			function newchat(){
				location.href="main.jsp";
			}
		</script>
	</head>
	<body onload="newchat()">



聊天主页面 main.jsp
<div id="contentDiv">
			<div id="titleDiv">
				<marquee direction="left" scrolldelay="200"><%=name%>..欢迎来到蝈蝈岛聊天室..o(∩_∩)o..
				</marquee>
			</div>
			<div id="chatDiv">
				<iframe src="chat.jsp" name="chat" frameborder="1" scrolling="auto"
					height="350px" width="470px">
				</iframe>
				<span id="divMess" style="width:240px;">请输入内容(100字以内)! </span>
				<div style="display:inline; text-align: right;width:200px"><a
					href="javascript:setAuto()">自动滚动</a>&nbsp;<a
					href="javascript:clearAuto()">取消滚动</a>
					&nbsp;<a href="dolayout.jsp">退出</a></div>
				<div>
					<textarea rows="3" cols="56" id="message" onclick="clearTxt()"></textarea>
				</div>
				<div style="text-align: right; padding-right: 0px;">
					<input type="button" value="确定" class="longBtn"
						onclick="sendMessage()">
				</div>
			</div>
			<div id="userDiv">
				<iframe src="user.jsp" name="userframe" frameborder="1" scrolling="auto"
					height="440px" width="110px">
				</iframe>
			</div>
		</div>


main.jsp的构成部分,是由 user.jsp和chat.jsp为主的页面构建起来的。
user.jsp是显示用户页面,chat.jsp是显示聊天信息显示页面,它们都是通过 iframe插入到 main.jsp中,但是在数据显示的处理方式上,我特意使用了两种方式。
对于chat.jsp 中几乎没有什么内容
<%@ page language="java" pageEncoding="GBK"%>

<html>
	<head>
		<style type="text/css">
			body{
				font-size:12px;
				margin:0px;
				line-height:18px;
				padding-left:5px;
				border:2px inset #eeebbb;
			}
		</style>
	</head>
	<body >
	</body>
</html>


但是 user.jsp中就不一样
<%@ page language="java" pageEncoding="GBK"%>

<html>
	<head>
		<style type="text/css">
			body{
				font-size:12px;
				margin:0px;
				line-height:18px;
				padding-left:5px;
			}
		</style>
		<script language="javascript">
				//用户请求数据
			var xmlRequestUser;
			
			//创建对象
			function createXMLHttpRequest(){
				if(window.XMLHttpRequest) { //Mozilla 浏览器
					return new XMLHttpRequest();
				}else if (window.ActiveXObject) { // IE浏览器
					try {
						return new ActiveXObject("Msxml2.XMLHTTP");
					} catch (e) {
						try {
							return new ActiveXObject("Microsoft.XMLHTTP");
						} catch (e) {}
					}
				}
			}
			function sendGetUserRequest(){
				var url = "getUser.jsp";
				xmlRequestUser = createXMLHttpRequest();
				xmlRequestUser.open("GET",url,true);
				xmlRequestUser.onreadystatechange = getUserResponse;
				xmlRequestUser.send(null);
			}
			
			function getUserResponse(){
				if(xmlRequestUser.readyState==4){
					if(xmlRequestUser.status ==200) {
						//信息已经成功返回
						showUserInfo();
					}else{
						showMess("您所请求的页面有异常!");
					}
				}
			}
			
			function showUserInfo(){
				var message = xmlRequestUser.responseXML.getElementsByTagName("userinfo")[0].firstChild.nodeValue;
				document.body.innerText = message;
			}
			//每隔10秒读取一次新用户
			window.setInterval("sendGetUserRequest()",1000);
		</script>
	</head>
	<body onload="sendGetUserRequest()">
		
	</body>
</html>


这两者的区别在于:
1.user.jsp中自己发送请求,去请求 getUser.jsp 页面,自己显示。
2.chat.jsp只负责显示,请求部分是 main.jsp页面完成的,在main.jsp中调用chat.jsp页面来显示异步请求后返回的数据。


main.jsp中调用chat.jsp显示聊天信息的数据时,有这一部分代码
	function showChatMessage(){
				var message = xmlRequest.responseXML.getElementsByTagName("message")[0].firstChild.nodeValue;
				document.frames("chat").document.body.innerText = message;
			}


document.frames("chat").document.body  就是来显示数据的。


//每隔三秒钟查询一次
			window.setInterval("sendRequest('doinput.jsp')",1000);
			var id ;
			function setAuto(){
				id = setInterval("document.frames('chat').document.body.scrollTop=document.frames('chat').document.body.scrollHeight",1000);
			}
			function clearAuto(){
				clearInterval(id);
			}


后面两个方法,就是用来解决聊天室信息总显示最后一行的,可以让滚动条自动滚动,也可以取消自动滚动,这个解决方法,很多人都在找,希望能看到这里。

以上是一部分关键代码,具体内容还是下载附件,功能的实现都不难,就是要细心的做一件事情比较困难。

源码环境 jdk1.6 + myeclipse ,tomcat 即可,无数据库,代码自写亲测,放心下载!



注意:IE版本不同,打开的效果可能有不一样,例如首页一打开,在有的IE浏览器上一闪就马上关闭了,不能看到登陆页面,如果是这种情况,请查看 index.jsp页面的代码

 
function openlogin(){   
                window.open("login.jsp","","width=600px,height=460px");   
               //window.opener=null;//注释掉这句话就可以了 ,但是会冒出一个提示让你关闭index.jsp页面 
                this.close();   
            } 


关于用户不是点击退出关闭连接退出聊天室,而是关闭窗体而退出聊天室,即时退出的效果已经解决,这个例子中没有写,但是之后就把这个功能实现了,给大家一个提示,实现起来也非常简单。

我下次可以在重新传一个上来。

提示就是:在关闭窗体 窗体卸载事件之前,使用xmlhttprequest对象发送一个请求到服务器端,清掉会话状态,且从application中清除此用户即可。


--- 关于上面的提示,我简单补充一下:关于窗体关闭的流程,不是单击关闭超级链接关闭,而是用户关闭浏览器时,触发事件,事件中请求服务器端清空用户即可。
//事件
	window.onunload = closeWindow;
			
			function closeWindow(){
				if(confirm("确认要退出聊天室吗?")){
					//发送请求到客户端删除此用户
					clearUser();
				}else{
                                              //已经点击了关闭浏览器操作 ,如果不退出就重新打开一次就可以了
					window.open("main.jsp","","width=600px,height=460px");
				}
			}
			
			function clearUser(){
				var url = "dolayout.jsp";
				xmlRequest = createXMLHttpRequest(); //创建对象
				//状态改变
				xmlRequest.onreadystatechange = clearStatus;
				xmlRequest.open("GET",url,true);
				xmlRequest.send(null);  // 发送请求
			}



清空登录信息 清空用户
<%@ page language="java" import="java.util.*" pageEncoding="GBK"%>
<%
	String user = (String)session.getAttribute("sessionuser");
	if(user==null){
		response.sendRedirect("index.jsp");
		return;
	}
	
	ArrayList<String> list = (ArrayList<String>)application.getAttribute("users");
	
	if(list!=null){
		for(String string:list){
			if(string.equals(user)){
				list.remove(string);
				application.setAttribute("users",list);
				break;
			}
		}
	}
	ArrayList<String> messageList = (ArrayList<String>)application.getAttribute("messages");
	
		if(messageList==null){
			messageList = new ArrayList<String>();
		}
		
	messageList.add("\n"+user+" , 离开聊天室了.....\n");
	application.setAttribute("messages",messageList);
		
	response.sendRedirect("index.jsp");
%>


例子重新上传一个吧!
大家如果要下载的话,就再去下载 20090412-ajax 无刷新聊天室-补充.rar
41
2
分享到:
评论
25 楼 KevinDai007 2014-11-13  
KevinDai007 写道
这个项目是不是我问题?我在我电脑上运行时,找开第二个窗口后,那么第一个窗口的人打字也会显示的是第二个人的名字,你们是这样吗?

我又试了下,用其它浏览器再打开一下就是正常的,也就是如果用同一种浏览器开两个窗口那么用户名就是会乱掉,用两个浏览器各运行一个窗口就是正常的
24 楼 KevinDai007 2014-11-13  
这个项目是不是我问题?我在我电脑上运行时,找开第二个窗口后,那么第一个窗口的人打字也会显示的是第二个人的名字,你们是这样吗?
23 楼 icemoon160 2012-06-22  
Great!
22 楼 walong2012 2012-06-16  
在呢么有运行了有问题
21 楼 syd6815892 2012-02-14  
没有java文件?
20 楼 wsldk 2011-10-30  
[size=medium][/size][color=brown][/color]赞一个,嘿嘿
19 楼 lovetangsong 2011-08-20  
Lz 非常感谢你发布得的demo,这对新手来说是非常不错案例。
但是经测试 不知道为什么你的demo 非得用http://localhost:8888/
而不能用http://192.xx.xx.xx:8888/ 访问?
18 楼 woshilifeng130 2011-01-05  
非常非常的感谢
17 楼 bcv0001 2010-11-25  
用很简单的方式实现聊天室功能,很适合新手,挺好的.
16 楼 lvp 2009-06-10  
river100200 写道

接触JSP&nbsp; 不久,有用到贵处的在线人数统计代码,发现如果用IE 刷新人数会一直+1 不知如何解决?还有不懂如何“使用xmlhttprequest对象发送一个请求到服务器端,清掉会话状态,且从application中清除此用户即可。”麻烦给个例子!先谢了~


关闭窗体的时候 可以触发一个事件 ,那么利用这个事件就完全来得及向服务器端发送一个请求,清掉会话。

window.onunload = closeWindow;

function closeWindow(){
if(confirm("确认要退出聊天室吗?")){
//发送请求到客户端删除此用户
clearUser();
}else{
window.open("main.jsp","","width=600px,height=460px");
}
}

function clearUser(){
var url = "dolayout.jsp";
xmlRequest = createXMLHttpRequest(); //创建对象
//状态改变
xmlRequest.onreadystatechange = clearStatus;
xmlRequest.open("GET",url,true);
xmlRequest.send(null);  // 发送请求
}
15 楼 river100200 2009-06-07  
接触JSP  不久,有用到贵处的在线人数统计代码,发现如果用IE 刷新人数会一直+1
不知如何解决?还有不懂如何“使用xmlhttprequest对象发送一个请求到服务器端,清掉会话状态,且从application中清除此用户即可。”麻烦给个例子!先谢了~
14 楼 lvp 2009-05-19  
li_na19870917ok 写道

有没有带数据库的例子啊?

抱歉 还没有,还没时间写这样的例子。
13 楼 li_na19870917ok 2009-05-12  
有没有带数据库的例子啊?
12 楼 qchong 2009-04-23  
每隔3s肯定不行,时间太长了。
11 楼 qchong 2009-04-23  
lvp 写道

qchong 写道
哎,又是轮询的,效果很不好建议用COMET实现


COMET 没怎么用过,应该出现的也很早,但觉得它没有Ajax那样通用。
如果采用Server push的话,服务器端JSP程序的响应一直不关闭,采用定时输出,弊端就是保持持续连接。并且一般的 HTTP WEB SERVER 无状态高并发的优点又如何保留呢?

当然这个例子也是通过 JS定时发送请求从服务器端取数据,不管有没有数据都请求,也占用了大量的带宽,个人觉得也确实不是一个很好的解决方式。

所以两者比较起来,只能说是各有利弊。

不过感谢提出的建议。

当然,这只是一个日常写的一个DEMO,对于初学者来说是可以看看,大家也可以讨论下,聊天室最好的实现方式。



主要是轮询的时间很难控制。长了不行,短了也不行。
10 楼 lvp 2009-04-16  
yhjhoo 写道

给楼主提个建议啊,login.jsp是处理逻辑层的地方,为什么不写成一个servlet或者说是一个struts里面的action呢?

jsp的本质也是servlet,这个小的DEMO是用纯的JSP手工完成的,所以所有的控制也是在JSP上进行处理的。可以用 servlet 实现,也可以使用 structs实现,应该来说不限于语言和框架。

只是本身写的一个小DEMO,有时间,我再改几个用 servlet和 structs 也可以,但本质也是一样的。

9 楼 yhjhoo 2009-04-16  
给楼主提个建议啊,login.jsp是处理逻辑层的地方,为什么不写成一个servlet或者说是一个struts里面的action呢?
8 楼 lvp 2009-04-15  
qchong 写道

哎,又是轮询的,效果很不好建议用COMET实现


COMET 没怎么用过,应该出现的也很早,但觉得它没有Ajax那样通用。
如果采用Server push的话,服务器端JSP程序的响应一直不关闭,采用定时输出,弊端就是保持持续连接。并且一般的 HTTP WEB SERVER 无状态高并发的优点又如何保留呢?

当然这个例子也是通过 JS定时发送请求从服务器端取数据,不管有没有数据都请求,也占用了大量的带宽,个人觉得也确实不是一个很好的解决方式。

所以两者比较起来,只能说是各有利弊。

不过感谢提出的建议。

当然,这只是一个日常写的一个DEMO,对于初学者来说是可以看看,大家也可以讨论下,聊天室最好的实现方式。

7 楼 qchong 2009-04-15  
哎,又是轮询的,效果很不好
建议用COMET实现
6 楼 ZZX19880809 2009-04-14  
进来看看再说!

相关推荐

Global site tag (gtag.js) - Google Analytics