网页聊天的基本原理很简单,在使用者发送讯息给伺服端时,同时取回新的聊天讯息,在使用者没有发送讯息,同时查询伺服端是否有新的讯息,并显示在页面中。
不过重点就在于取得讯息或重新取得讯息的方式,在过去,是在让浏览器定时重新整理网页,每一次除了新的讯息之外,往往伴随著大量重复的HTML标签等内容。
如果使用非同步请求,取得XML回应讯息,并动态更新页面中显示聊天讯息的部份,这么一来,就可以节省掉下载重复页面内容的频宽,使用者的画面也会更稳定,不会因为重新整理而发生闪烁的感觉。
例如,您可以写一个简单的聊天页面:
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=BIG5"> <title>聊天室</title> <script type="text/javascript" src="ChatRoomEx-1.js"></script> </head> <body> <p> 输入讯息: <input id="text"/> <input type="button" value="传送" οnclick="sendMessage()"/> </p>
<p>聊天室讯息:</p> <table align="left"> <tbody id="dynamicUpdateArea"></tbody> </table> </body> </html>
您可以在这个页面中的栏位中输入文字,而下方会有个显示讯息的区域,每次的新讯息将只在该区域更新,页面中其余的部份不用变动,所以不用重复下载,来看一下JavaScript的部份:
var xmlHttp;
function createXMLHttpRequest() { if (window.ActiveXObject) { xmlHttp = new ActiveXObject("Microsoft.XMLHTTP"); } else if (window.XMLHttpRequest) { xmlHttp = new XMLHttpRequest(); } }
function sendMessage() { var msg = document.getElementById("text").value;
// 使用者只是随意按下传送钮,但文字栏位中没有文字 if(msg === "") { // 那就重新整理讯息区好了 refreshMessage(); return; }
// 传送讯息 var param = "task=send&msg=" + msg; // ajax请求 ajaxRequest(param); // 清空文字栏位 document.getElementById("text").value = ""; }
// 定时查询用这个 function queryMessage() { var param = "task=query"; ajaxRequest(param); }
function ajaxRequest(param) { var url = "ChatRoomServlet?timeStamp=" + new Date().getTime(); createXMLHttpRequest(); xmlHttp.onreadystatechange = refreshMessage; xmlHttp.open("POST", url); xmlHttp.setRequestHeader("Content-Type", "application/x-www-form-urlencoded;"); xmlHttp.send(param); }
function refreshMessage() { if(xmlHttp.readyState == 4) { if(xmlHttp.status == 200) { // 处理显示讯息的表格区域 var table_body = document.getElementById("dynamicUpdateArea"); var length = table_body.childNodes.length; var i; for (i = 0; i < length; i++) { // 先移除原有的列(row) table_body.removeChild(table_body.childNodes[0]); }
// 处理取回的讯息 var messages = xmlHttp.responseXML.getElementsByTagName("message"); length = messages.length; for(i = length - 1; i >= 0 ; i--) { var message = messages[i].firstChild.data; // 在表格中新增一列来排列讯息 var row = createRow(message); table_body.appendChild(row); } // 下次2秒后会再查询一下有无新讯息 setTimeout(queryMessage, 2000); } } }
function createRow(message) { var row = document.createElement("tr"); var cell = document.createElement("td"); var cell_data = document.createTextNode(message); cell.appendChild(cell_data); row.appendChild(cell); return row; }
伺服端必须传回以下的XML格式,表示目前伺服端所管理的聊天室中可取得的讯息:
<messages> <message>聊天讯息一</message> <message>聊天讯息二</message> <message>聊天讯息三</message> </messages>
以下是个简单的聊天室Servlet:
package onlyfun.caterpillar;
import java.io.IOException; import java.io.PrintWriter; import java.util.LinkedList; import java.util.List;
import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse;
public class ChatRoomServlet extends javax.servlet.http.HttpServlet implements javax.servlet.Servlet { private static LinkedList<Message> messages = new LinkedList<Message>();
private List<Message> addMessage(String text) { if (text != null && text.trim().length() > 0) { messages.addFirst(new Message(text)); while (messages.size() > 10) { messages.removeLast(); } }
return messages; }
private List<Message> getMessages() { return messages; }
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { List<Message> list = null;
if("send".equals(request.getParameter("task"))) { String msg = request.getParameter("msg"); // 中文处理 msg = new String(msg.getBytes("ISO-8859-1"), "UTF8"); list = addMessage(msg); } else if("query".equals(request.getParameter("task"))){ list = getMessages(); }
response.setContentType("text/xml"); response.setHeader("Cache-Control", "no-cache"); response.setCharacterEncoding("UTF8");
PrintWriter out = response.getWriter(); out.println("<messages>"); for(int i = 0; i < list.size(); i++) { String msg = list.get(i).getText(); out.println("<message>" + msg + "</message>"); } out.println("</messages>"); out.close(); } }
package onlyfun.caterpillar;
public class Message { private String text;
public Message(String newtext) { text = newtext; if (text.length() > 256) { text = text.substring(0, 256); } text = text.replace('<', '['); text = text.replace('&', '_'); }
public String getText() { return text; } }
|
<script src="http://pagead2.googlesyndication.com/pagead/show_ads.js" type="text/javascript">
</script>
<iframe width="160" scrolling="no" height="600" frameborder="0" allowtransparency="true" hspace="0" vspace="0" marginheight="0" marginwidth="0" src="http://pagead2.googlesyndication.com/pagead/ads?client=ca-pub-9750319131714390&dt=1179111675171&lmt=1177046893&format=160x600_as&output=html&url=http%3A%2F%2Fcaterpillar.onlyfun.net%2FGossip%2FAjaxGossip%2FChatRoom.html&ad_type=text_image&ref=http%3A%2F%2Fcaterpillar.onlyfun.net%2FGossip%2FAjaxGossip%2FAjaxGossip.html&cc=15&flash=9&u_h=768&u_w=1024&u_ah=740&u_aw=1024&u_cd=16&u_tz=480&u_his=2&u_java=true&u_nplug=18&u_nmime=58" name="google_ads_frame"></iframe>
<script src="http://pagead2.googlesyndication.com/pagead/show_ads.js" type="text/javascript">
</script>
<iframe width="160" scrolling="no" height="600" frameborder="0" allowtransparency="true" hspace="0" vspace="0" marginheight="0" marginwidth="0" src="http://pagead2.googlesyndication.com/pagead/ads?client=ca-pub-9750319131714390&dt=1179111675187&lmt=1177046893&prev_fmts=160x600_as&format=160x600_as&output=html&url=http%3A%2F%2Fcaterpillar.onlyfun.net%2FGossip%2FAjaxGossip%2FChatRoom.html&ad_type=text_image&ref=http%3A%2F%2Fcaterpillar.onlyfun.net%2FGossip%2FAjaxGossip%2FAjaxGossip.html&cc=15&flash=9&u_h=768&u_w=1024&u_ah=740&u_aw=1024&u_cd=16&u_tz=480&u_his=2&u_java=true&u_nplug=18&u_nmime=58" name="google_ads_frame"></iframe> |