最近在网上看见这个, 就拿过来以待以后研究..
BS的聊天系统,一般两种技术(各有优劣):
1、基于Ajax或子页面刷新的拉模型;
2、基于HTTP长连接的推模型。
因为Ajax的“拉模型”,比较常见,这里给出基于HTTP长连接的“推模型”:
由三个页面组成:
chatmain.jsp 框架页面,引入两个Frame页面,并负责创建聊天室;
chatwnd.jsp 聊天信息窗口,负责用服务器推的方式显示所有人的聊天信息;
chatsender.jsp 聊天发送窗口,负责发送自己想说的话。
注意:本样例主要是为了说明 “基于长连接的推模型”,在聊天室完备性上完全不具有参考意义。
【chatmain.jsp 框架页面】
[html] view plaincopyprint?
1. <%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
2. <%@ page import="java.util.*"%>
3. <%
4. // 这三句话只是为了防止浏览器缓存
5. response.setHeader("Cache-Control", "no-cache, no-store"); //HTTP 1.1
6. response.setHeader("Pragma", "no-cache"); //HTTP 1.0
7. response.setDateHeader("Expires", 0); //prevents caching at the proxy server
8.
9.
10. // 检查并按需创建“聊天室”
11. ArrayList<String> lstMsg = (ArrayList<String>) application.getAttribute("room");
12. if (lstMsg == null) {
13. application.setAttribute("room", new ArrayList<String>());
14. }
15. %>
16. <html>
17. <frameset rows="80%, 20%">
18. <frame src="chatwnd.jsp"></frame>
19. <frame src="chatsender.jsp"></frame>
20. </frameset>
21. </html>
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ page import="java.util.*"%>
<%
// 这三句话只是为了防止浏览器缓存
response.setHeader("Cache-Control", "no-cache, no-store"); //HTTP 1.1
response.setHeader("Pragma", "no-cache"); //HTTP 1.0
response.setDateHeader("Expires", 0); //prevents caching at the proxy server
// 检查并按需创建“聊天室”
ArrayList<String> lstMsg = (ArrayList<String>) application.getAttribute("room");
if (lstMsg == null) {
application.setAttribute("room", new ArrayList<String>());
}
%>
<html>
<frameset rows="80%, 20%">
<frame src="chatwnd.jsp"></frame>
<frame src="chatsender.jsp"></frame>
</frameset>
</html>
【chatwnd.jsp 聊天信息窗口】
[html] view plaincopyprint?
1. <%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
2. <%@ page import="java.util.*"%>
3. <%@ page import="java.io.*"%>
4. <html>
5. <h2>Welcome to the ChatRoom!</h2>
6. <%
7. // 这三句话只是为了防止浏览器缓存
8. response.setHeader("Cache-Control", "no-cache, no-store"); //HTTP 1.1
9. response.setHeader("Pragma", "no-cache"); //HTTP 1.0
10. response.setDateHeader("Expires", 0); //prevents caching at the proxy server
11.
12.
13. Integer myMsgPos = 0; // 记录最后显示消息的位置
14. ArrayList<String> lstMsg = (ArrayList<String>) application.getAttribute("room");
15. out.flush(); // 把之前的HTML信息都刷出去
16. PrintWriter pw = response.getWriter(); // 用PrintWriter取代JspWriter以便于检查Socket异常
17. try {
18. synchronized (lstMsg) {
19. while (true) { // 死循环,也就意味着本次HTTP请求不会结束
20. if (lstMsg.size() > myMsgPos) { // 检查有没有新的消息
21. // 循环显示所有新消息
22. System.out.println("Got new msg to push: "+(lstMsg.size()-myMsgPos));
23. for (int i = myMsgPos; i < lstMsg.size(); i++) {
24. String msg = lstMsg.get(i);
25. pw.write("<p>"+msg+"</p>");
26. }
27. if (pw.checkError()) { // 强制将输出缓冲区内容发送出去,并检查异常
28. System.out.println("IOException detected, JSP end.");
29. break; // 说明已经发生IOException(一般是浏览器页面关掉了)
30. }
31. myMsgPos = lstMsg.size(); // 更新最后显示的消息位置
32. }
33. System.out.println("Waiting new msg..." + out.getRemaining());
34. lstMsg.wait(); // 临时释放对lstMsg的独占(同步锁),等待新消息的到来
35. }
36. }
37. } catch (Exception ex) {
38. System.out.println(ex);
39. }
40. %>
41. </html>
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ page import="java.util.*"%>
<%@ page import="java.io.*"%>
<html>
<h2>Welcome to the ChatRoom!</h2>
<%
// 这三句话只是为了防止浏览器缓存
response.setHeader("Cache-Control", "no-cache, no-store"); //HTTP 1.1
response.setHeader("Pragma", "no-cache"); //HTTP 1.0
response.setDateHeader("Expires", 0); //prevents caching at the proxy server
Integer myMsgPos = 0; // 记录最后显示消息的位置
ArrayList<String> lstMsg = (ArrayList<String>) application.getAttribute("room");
out.flush(); // 把之前的HTML信息都刷出去
PrintWriter pw = response.getWriter(); // 用PrintWriter取代JspWriter以便于检查Socket异常
try {
synchronized (lstMsg) {
while (true) { // 死循环,也就意味着本次HTTP请求不会结束
if (lstMsg.size() > myMsgPos) { // 检查有没有新的消息
// 循环显示所有新消息
System.out.println("Got new msg to push: "+(lstMsg.size()-myMsgPos));
for (int i = myMsgPos; i < lstMsg.size(); i++) {
String msg = lstMsg.get(i);
pw.write("<p>"+msg+"</p>");
}
if (pw.checkError()) { // 强制将输出缓冲区内容发送出去,并检查异常
System.out.println("IOException detected, JSP end.");
break; // 说明已经发生IOException(一般是浏览器页面关掉了)
}
myMsgPos = lstMsg.size(); // 更新最后显示的消息位置
}
System.out.println("Waiting new msg..." + out.getRemaining());
lstMsg.wait(); // 临时释放对lstMsg的独占(同步锁),等待新消息的到来
}
}
} catch (Exception ex) {
System.out.println(ex);
}
%>
</html>
【chatsender.jsp 聊天发送窗口】
[html] view plaincopyprint?
1. <%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
2. <%@ page import="java.util.*" %>
3. <%@ page import="java.text.*" %>
4. <%
5. SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss");
6. // 这三句话只是为了防止浏览器缓存
7. response.setHeader("Cache-Control", "no-cache, no-store"); //HTTP 1.1
8. response.setHeader("Pragma", "no-cache"); //HTTP 1.0
9. response.setDateHeader("Expires", 0); //prevents caching at the proxy server
10.
11.
12. // 获取POST的“发言”
13. String msg = request.getParameter("msg");
14. if (msg!=null && msg.length()>0) {
15. msg = "[" + sdf.format(new Date()) + "]" + session.getId() + ": " + msg;
16. System.out.println(msg); // 调试跟踪用的信息
17. ArrayList<String> lstMsg = (ArrayList<String>) application.getAttribute("room");
18. synchronized (lstMsg) {
19. lstMsg.add(msg); // 消息放入池中(消息太多会溢出的,这里并没有进行处理)
20. lstMsg.notifyAll(); // 通知chatwnd.jsp们,有新的消息来了,可以推给浏览器了
21. }
22. }
23. %>
24. <!DOCTYPE unspecified PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
25. <html>
26. <form method="post">
27. My Speak: <input name="msg" type="text" size="40" /> <input type="submit" />
28. </form>
29. </html>