Ajax程序模拟SOCKET套接字编程
Ajax, 套接字, SOCKET, 程序, 模拟
在家无聊,重新看了下AJAX最底层的原理,因为不喜欢一直用被人封装了不知道几层的东西,所以就自己写了个不倚靠其他函数库的程序。并且用一个网页上的无刷新聊天室做例子来模拟SOCKET套接字编程。前台没的说,肯定用JavaScript。后台我用了自己熟悉的ASP.NET(C#)。其实于语言无关,各位可以用自己的解决方案轻松移植。(应该等能接受GET和POST请求吧,呵呵。我这是废话)
客户端核心代码。
<div id="divChatingDisplay"></div>做为实时显示聊天内容的区域
<p>
姓名:<input id="txtName" style="width: 100px" type="text" />说:
<input id="txtContent" type="text" style="width: 500px" />
<input id="Sender" type="button" value="发送" /></p>
一个简单的控制界面,这里不涉及用户身份验证系统。(史上最简陋的界面)
function createXMLHttpRequest()
{
if(window.ActiveXObject)
{
xmlHttp = new ActiveXObject("Microsoft.XMLHTTP");
}
else if(window.XMLHttpRequest)
{
xmlHttp = new XMLHttpRequest()
}
}
构件一个XML形式的HTTP请求,默认大家用的都是IE浏览器(在中国最流行的一种)
function SendChatMessage()
{
createXMLHttpRequest();
var strName = txtName.value;
var strContent = txtContent.value;
var url = "bg_ChatInput.aspx";
var form = "name=" + strName + "&content=" + strContent;
xmlHttp.open("POST",url,true);
xmlHttp.setRequestHeader("Content-Type","application/x-www-form-urlencoded");
xmlHttp.send(form);
}
模拟浏览器发送POST的请求,在POST里写上各个需要传到服务器端的内容,发言的人和发言的内容。出于考虑到有些人可能话比较多,所以没有GET请求。POST传输的数据量最多达到4G,应该不会有那么话多人的,呵呵(真有一句话超过4GB的就踢了)。这里的bg_ChatInput.aspx是后台把聊天内容放进数据库的后台程序。下文会介绍它的实现方法。
function GetMessage()
{
createXMLHttpRequest();
var url = "bg_ChatGetInfo.aspx"
xmlHttp.onreadystatechange = handleStateChange;
xmlHttp.open("GET",url,true);
xmlHttp.send(null);
Refresh();
}
实时获得消息的函数,因为不涉及任何要传输的参数,所以用GET请求。bg_ChatGetInfo.aspx是后台一直获取消息的函数。处理收到的消息使用handleStateChange函数,这里用函数指针把.onreadystatechange 事件触发的函数指向handleStateChange。
function handleStateChange()
{
if(xmlHttp.readyState == 4)
{
if(xmlHttp.status == 200)
{
var content = xmlHttp.responseText;
divChatingDisplay.innerHTML=content;
}
else
{
window.alert("An error occurred: " + xmlHttp.statusText);
}
}
}
这里包含了一个简单的错误处理,详细情况可以参考一下关于微软的XMLHttpRequest对象的参考文档,这里没有什么处理,就是把接受到的数据简单得写入到divChatingDisplay中,并且以HTML格式。至此,我们的客户端还差一个定期查询更新内容的函数来模拟SOCKET中的监听线程。
function Refresh()
{
window.setTimeout("GetMessage()",1000);
}
在HTML头上添加<body>
我们注意,在刚刚的GetMessage函数最后套用了Refresh()函数,这样每1000毫秒都会执行一次GetMessage()函数,就模仿了SOCKET中的监听线程所做的工作,这样可以每次都能收到最新消息(这样放在另一个函数,而不是放在GetMessage函数里是让页面载入1秒后才开始获取信息,让界面充分载入后再加载数据,看上去比较高级哈.别把界面弄得太复杂,否则就看不出效果了)。这是客户端最需要注意的技巧。接下来我们完成了客户端所有的开发工作。
服务器端开发
<%@ Page Language="C#" %>
<%@ Import Namespace="System.Data.OleDb" %>
<script runat="server">
protected void Page_Load(object sender, EventArgs e)
{
string strName = Request.Form["name"].ToString();
string strContent = Request.Form["content"].ToString();
string strTime = DateTime.Now.ToString("T");
string connString = @"Provider=Microsoft.Jet.OLEDB.4.0; Data Source=" + AppDomain.CurrentDomain.BaseDirectory + @"App_Data/db_InfoExchange.mdb";
string strSql = string.Format("INSERT INTO [tb_Chat] ([sender],[content],[time]) VALUES ('{0}','{1}','{2}')",strName,strContent,strTime);
OleDbConnection conn = new OleDbConnection(connString);
OleDbCommand cmd = new OleDbCommand(strSql, conn);
conn.Open();
int res = cmd.ExecuteNonQuery();
conn.Close();
}
</script>
一个很粗糙的ASP.NET程序,不需要任何界面.选用ACCESS做数据库,数据库设计也不介绍了.收到POST请求后获取name和content两个数据项后生成一个insert语句。添加到数据库中,注意我们接受刚刚用JavaScript模拟出来的POST请求。bg_ChatInput.aspx完成。
<%@ Page Language="C#" %>
<%@ Import Namespace="System.Data.OleDb" %>
<script runat="server">
public int count = 0;
protected void Page_Load(object sender, EventArgs e)
{
Response.AddHeader("Cache-control", "No-cache");
Response.AddHeader("Pragma", "No-cache");
string connString = @"Provider=Microsoft.Jet.OLEDB.4.0; Data Source=" + AppDomain.CurrentDomain.BaseDirectory + @"App_Data/db_InfoExchange.mdb";
string strSql = "SELECT * FROM [tb_Chat]";
OleDbConnection conn = new OleDbConnection(connString);
OleDbCommand cmd = new OleDbCommand(strSql, conn);
OleDbDataReader reader;
conn.Open();
reader = cmd.ExecuteReader();
while (reader.Read())
{
Response.Write("<p><font size=2>"+reader["sender"].ToString() + " 说:" + reader["content"].ToString() + "</font></p>");
count++;
}
reader.Close();
if (count >= 10)
{
string sqlDelete = "DELETE FROM [tb_Chat]";
OleDbCommand cmdDelete = new OleDbCommand(sqlDelete, conn);
int redDelete = cmdDelete.ExecuteNonQuery();
}
conn.Close();
}
</script>
这里需要特别注意的是一个烦恼了我很长时间的问题,虽然我每次都有新的请求发向服务器,但客户端并没有每次都更新数据,经过我发疯一样得寻找原因发现原来是浏览器太高级了,习惯从缓存中获取数据。(避免频繁得向服务器要信息,再成堵塞.可我要的就是这个效果呀)为了每次都确保它能从服务器获取新的更新过的数据,必须要在文件前追加两个语句:
Response.AddHeader("Cache-control", "No-cache");
Response.AddHeader("Pragma", "No-cache");
不同的后台程序都不一样,大家可以自己查看自己语言的写法,每个后台都有类似的功能。(具体怎么回事情,大家可以翻HTTP协议的原始定义)
接着要说明的是,每次获取更新请求后(每个用户都会每阁1秒发一次请求)都从数据库里获取信息,并追加修饰,确定字体大小,颜色等等,因为客户端的数据处理只是简单得写入DIV中,所以一切按照HTML标准就可以了。这里为了防止大量的历史数据,每增加10条就会把数据库清空,我承认这不是一个很好的用户体验。但ACCESS数据库的支持实在不怎么样。诸如:
Delete top(1) from tb_Chat
的SQL语句是不支持的,所以只能一古脑都删干净咯,如果你使用其他格式的数据库就会解决这个问题。
至次,模拟了SOCKET套接字编程。一来一去都有,而且实时更新数据,模拟了监听进程。这一切都被很好的保护着。用户不会感到一丝的不自然,整个页面没有任何频闪,只有别人聊天记录被刷新,而且没有帕塔帕塔的刷新的声音,很安静,很智能。(像是某洗衣机的广告词,呵呵)。给志同道合的朋友们做个交流,呵呵。