65.9K
CodeProject 正在变化。 阅读更多。
Home

简单的网络聊天

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.64/5 (9投票s)

2013年4月13日

CPOL

7分钟阅读

viewsIcon

52724

downloadIcon

2401

使用 PHP 和 MySQL 实现简单的网络聊天

引言

自从即时通讯工具(或者Yahoo! Messenger和Google Talk还活着吗?)消亡以来,网络聊天已成为互联网上最常用的交流媒介。各种社交网络、客户服务网站和论坛都利用这项技术来发展、增强并提高其网站和产品的效率和生产力。

从零开始实现一个网络聊天可能非常棘手(有些人甚至不知道从何开始)。然而,本文将展示如何使用PHP和MySQL从零开始构建一个简单的网络聊天。这包括:

  1. 注册和登录平台 
  2. 实时聊天平台
  3. 离线消息和检索 

最低要求 

此示例的基本要求是HTML5浏览器,当然还需要一个运行PHP和MySQL的Web服务器Smile

设置(和假设) 

为了代码的简洁和清晰,做了一些假设

  • 与您的Web服务器和MySQL服务器的连接永不失败,因此代码中没有内置错误处理程序
  • 只包含了一个与代码顺畅运行相关的基本表单验证
  • 由于使用了PHP的strtotime()函数,在夏令时期间,聊天时间可能会提前1小时。没有采取任何措施使其返回本地时间

重要提示: 代码的编写方式使得数据库和表由代码生成。您只需在“ext.inc”文件中提供MySQL凭据和“frame.php”的路径(这非常重要,否则代码可能无法工作)。

如果项目文件位于Web服务器的www目录下,则“ext.inc”文件中的$iframe_url应设置为“/frame.php”。但如果项目文件位于www目录中的一个名为“webchat”的文件夹中,$iframe_url应设置为“/webchat/frame.php”。

数据库和表

代码会在您的MySQL服务器上创建一个名为“webchat”的数据库(如果您将“ext.inc”文件中的$sql_database_name变量保持不变),并同时创建四个表:“chat”、“online”、“reg_info”和“archive”。 

表:chat 和 archive

“chat”和“archive”两个表是相同的,都包含七列用于存储聊天信息。“chat”表存储未读聊天,“archive”表存储可以在未来检索的旧聊天。

下表展示了表的结构

chat_id: 每条消息都有一个ID。这有助于将来轻松定位。
sender_id: 发送聊天的人的用户ID
receiver_id: 接收聊天的人的用户ID
content: 聊天内容
time: 聊天时间
status: 聊天是否已接收。默认为“未读”。这有助于识别要归档的聊天。
mime: 如果您想在聊天中启用图片、语音、表情符号等,这可以作为聊天内容的MIME类型。


 

(chat表的结构)


表:online

“online”表包含2列,用于监控在线用户:“id”和“time”。您可以阅读我的文章获取在线用户数量及其他功能,以更深入地了解如何监控在线用户。基本上,这个表会定期从客户端更新,以了解谁在线。

id: 注册用户的用户ID
time: 用户最后一次出现的时间(基本上是该行最后一次从客户端更新的时间)

表:reg_info

reg_info 表包含四列,用于存储用户的注册信息。

id: 用户的用户ID。由MySQL服务器自动生成(auto_increment)
username: 用户的用户名。必须是唯一的。
displayname: 在聊天会话期间显示的名称
password:: 密码。经过crypt()加密,请记住要 crypt() 您的密码,而不是 md5()。

(reg_info表的结构)

代码和解释 

注册和登录平台

注册和登录平台都只是普通的表单提交,没什么大不了的。但请记住,只内置了一点表单验证,所以这不应该在实际中使用。用户ID在注册时使用MySQL的AUTO_INCREMENT自动生成。

实时聊天平台

聊天实际上需要向服务器发送和从服务器获取消息。为此考虑了各种新技术,但旧的AJAX赢得了这场战斗。

 

为什么没有使用SSE(Server Sent Event)

SSE是HTML5推出的一种新的推送技术。它最适合这项工作,只是IE浏览器不支持它(至少目前还不支持)。如果您确定您的客户端都不会使用IE,那么可以使用它。

为什么没有使用Websocket

Websocket可能是所有推送技术中最强大的,但请记住,您必须拥有支持websocket协议(ws://)的服务器才能使用它。此外,它在IE10中还处于原型阶段(只是一个插件)。


选择AJAX是因为您不必重新配置服务器(您甚至可能无法访问),并且它受所有浏览器(至少是我所知道的)支持。

聊天算法 

在线用户列表 

在线用户列表是根据文章获取在线用户数量及其他功能中讨论的算法生成的,该算法基本上是让客户端(浏览器)以一定时间间隔更新服务器上的数据库表(本例中为“online”表),并获取行更新的最后时间实例,以判断拥有该行的用户是否在线。

(在线用户列表)

iframes 用于保存每个聊天会话。当以下任何事件发生时,函数 addNew() 会动态生成 iframe:点击在线用户,收到离线消息,或在线用户(其 iframe 尚未创建)向您发送消息。

.
.
.
function addNew(oid)
{
	if(!document.getElementById("iPan" + oid ))
	{
		var r = document.createElement('div');
		//For iframe
		r.id = "c" + oid;
		r.className = "iFrmPad";
		r.draggable = "true";
		r.innerHTML =	"<div style="background: rgb(0, 0, 255); padding: 3px; height: 20px; color: rgb(255, 255, 255); font-size: 20px;">" + arguments[1] + " </div>";
		r.innerHTML += "<iframe class='iFrm' src='?oid=" + oid + "' id='iPan" + oid + "' style='border:0'></iframe>";
		r.innerHTML += "<input class=input placeholder='Gotta say that' type=text id='chatInput" + oid + "'>";
		r.innerHTML += "<input title='Say It' type=button value='Go' onclick=sendMsg('" + oid + "'); >";
		anchor = document.getElementById("anchor");
		document.body.insertBefore(r, anchor);
	}
}
.
.
.
 

父文档 (mpage.php) 和 iframe 使用 HTML5 的 postMessage() 和 addEventListener() 进行通信。对方的用户 ID 嵌入在 iframe、输入字段和发送按钮(Go 按钮)的 ID 中。

发送算法

按下发送按钮(Go 按钮)时

  1. 调用 sendMsg() 函数,将接收聊天的客户端用户ID作为其参数。
  2. sendMsg() 简单地从输入字段中获取聊天内容,使用用户 ID,并通过 AJAX 将聊天内容连同发送方和接收方用户 ID(您的 ID 和对方的 ID)发送到服务器。
  3. 服务器将聊天存储在“chat”表中,并通过发送包含发送者用户ID、显示名称、聊天内容和聊天时间的JSON来通知AJAX已收到消息。
  4. sendMsg() 评估 JSON 并将必要数据转发给 revMsg(),同时将聊天输入字段设为空。
  5. revMsg() 将收到的信息包装成HTML,并使用HTML5的postMessage()将其发送到相应的iframe(使用参数中收到的发送者ID来定位iframe,请记住用户ID嵌入在iframe的ID中)。
  6. iframe 通过 HTML5 的 addEventListener() 接收消息,检查以确定消息来源(这对于防止未经授权访问您的网站非常重要),并显示消息。
.
.
. 
function sendMsg(id)
{
	msg = document.getElementById("chatInput" + id).value;
	i = new XMLHttpRequest();
	urli = "backend.php?send&uid=" + document.getElementById('uid').value + "&oid=" + id + "&msg=" + msg;
	i.open("POST", urli, true);
	i.onreadystatechange=function()
			{
				if(i.readyState==4 && i.status==200)
				{
					rex = eval("(" + i.responseText + ")");
					revMsg(rex.id, rex.displayname, rex.content, rex.time);
					document.getElementById("chatInput" + id).value = "";
				}
			}
		i.send();
}

function revMsg(oid, displayname, content, time)
{
	o = document.getElementById('iPan' + oid);
	sty = "background:#aabb00; width:95%; float:right; margin-top:3px;";
	if(arguments[4] == 1)sty = "background:#ffcc00; width:95%; float:left; margin-top:3px;";
	details = "<div>" + displayname + " " + content + " " + time + "</div>";
	
	if(o == null)
	{//i.e. new chat
		addNew(oid, displayname);
		return;
	}
	o.contentWindow.document.getElementById("chatPan");
	o.contentWindow.postMessage(details,'');
 }
.
.
.

接收算法

在iframe中,AJAX用于每5秒向服务器发送ping请求,以获取新消息。

  1. pingServer() 将用户 ID 发送到服务器。
  2. 服务器在“chat”表中扫描 receiver_id 等于用户 ID 且 status 等于“Not Read”的聊天记录。
  3. 服务器以JSON格式响应找到的结果。
  4. 服务器将所有结果标记为“已读”,即,将状态列从“未读”更改为“已读”,并将所有结果移动到“archive”表。
  5. pingServer 接收 JSON,评估它并将必要的数据发送到 revMsg()。
  6. 执行“发送算法”的步骤 5 – 步骤 6。

pingServer() 被设计为从 iframe 而不是从父文档运行,以确保 iframe 的 DOM 在尝试访问它之前已完全加载。

.
.
.
function pingServer()
{
	j = new XMLHttpRequest();
	urlj = "backend.php?ping&uid=" +  + "&oid=" + ;
	j.open("POST", urlj, true);
	j.onreadystatechange=function()
			{
				if(j.readyState==4 && j.status==200)
				{
					var er = eval("(" + j.responseText + ")");//Receive JSON; format {'a0':2,'a1':{'id':'234','displayname':'a-friend','content':'Hello world','time':'2013-03-15 10:16:57'},'a2':{'id':'567','displayname':'b-friend','content':'Hello world again','time':'2013-03-15 11:16:57'}}
					counter = er.a0;
					while(counter > 0)
					{
						x = er['a' + counter];
						parent.revMsg(x.id, x.displayname, x.content, x.time, 1);
						counter--;
					}
				}
			}
	j.send();
}
 
//query server for msg every 5sec
setInterval(pingServer, 5000);
.
.
.

(聊天会话)


离线消息和检索 

离线消息没有额外的代码或概念。您可以给任何在线或不在线的人发送消息。离线消息检索通过函数 scanner 实现,该函数向服务器发出信号,扫描“chat”表中状态为“未读”的聊天记录。服务器响应用户ID列表,scanner() 调用 addNew() 创建他们的 iframe,然后“接收算法”启动。这适用于离线消息和由对方发起的私人消息。

.
.
.
function scanner()
{
	x = new XMLHttpRequest();
	urlx = "backend.php?scan&uid=" + ;
	x.open("POST", urlx, true);
	x.onreadystatechange=function()
			{
				if(x.readyState==4 && x.status==200)
				{
					erx = eval( "(" + x.responseText + ")" ); //JSON format {'a0':2, 'a1':'{'id':'2','displayname':'Whalleh'}', 'a2':'{'id':'23','displayname':'Osofem'}'}
					counter = erx.a0;
					while(counter > 0)
					{
						r = erx['a' + counter];
						addNew(r.id, r.displayname);
						counter--;
					}
				}
			}
	x.send();
}

setInterval(scanner, 5000);
.
.
.
 

进一步开发

这可以进一步开发,以包含 

  1. 对方正在打字时的提示 
  2. 用户离开浏览器时新消息的提醒
  3. 用户长时间离开浏览器时将其状态更改为离开模式
  4. 从存档加载更早的消息
  5. 表情符号 
© . All rights reserved.