Web 通信 之 长连接、长轮询(long polling)
基于HTTP的长连接,是一种通过长轮询方式实现”服务器推”的技术,它弥补了HTTP简单的请求应答模式的不足,极大地增强了程序的实时性和交互性。
1.
轮询:客户端定时向服务器发送Ajax请求,服务器接到请求后马上返回响应信息并关闭连接。
优点:后端程序编写比较容易。
缺点:请求中有大半是无用,浪费带宽和服务器资源。
实例:适于小型应用。
2.
长轮询:客户端向服务器发送Ajax请求,服务器接到请求后hold住连接,直到有新消息才返回响应信息并关闭连接,客户端处理完响应信息后再向服务器发送新的请求。
优点:在无消息的情况下不会频繁的请求,耗费资源小。
缺点:服务器hold连接会消耗资源,返回数据顺序无保证,难于管理维护。
实例:WebQQ、Hi网页版、Facebook IM。
3.
长连接:在页面里嵌入一个隐蔵iframe,将这个隐蔵iframe的src属性设为对一个长连接的请求或是采用xhr请求,服务器端就能源源不断地往客户端输入数据。
优点:消息即时到达,不发无用请求;管理起来也相对方便。
缺点:服务器维护一个长连接会增加开销。
实例:Gmail聊天
实例代码:
客服端:
<1>AJ04custserv.php
<?php
setcookie('username','admin');
?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" lang="zh-CN">
<head>
<title>新建网页</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<script type="text/javascript">
//在客服系统端打印用户发送的的信息,msg是在AJ04server.php
//中封装过的数据,可以直接访问
function comet(msg){
var cont ='';
cont += '<a onclick="replay(\''+msg.pos+'\')">'+msg.pos +'</a>'+'对你说<br />';
cont += msg.content+'<br />';
document.getElementById('msgzone').innerHTML +=cont;
}
//点击不同用户名的响应事件
function replay(pos){
document.getElementById('rec').innerHTML=pos;
}
//发送信息到用户
xhr=new XMLHttpRequest();
function huifu(){
var rec=document.getElementById('rec').innerHTML;
var cont=document.getElementsByTagName('textarea')[0].value;
//判断接受者或者信息是否为空
if(rec==''||cont==''){
alert('回复人且内容不能为空!');
return;
}
//打开通向服务器的通道。
xhr.open('POST','AJsendmsg.php',true);
//设置头信息
xhr.setRequestHeader('content-type','application/x-www-form-urlencoded');
//根据服务器端的状态码执行回调函数
xhr.onreadystatechange=function(){
if(this.readyState==4){
if(this.responseText=='OK'){
//接受数据成功需要执行的操作
var rep ='';
rep += '你对'+rec+'说<br />';
rep += cont+'<br />';
//在网页上打印信息
document.getElementById('msgzone').innerHTML+=rep;
//清空对话框信息
document.getElementsByTagName('textarea')[0].value='';
}
}
}
xhr.send('rec='+rec+'&content='+cont);
}
</script>
<style type="text/css">
#msgzone{
border: 1px solid gray;
width:300px;
height:400px;
overflow:auto;
}
</style>
</head>
<body>
<h1>Ajax实时聊天之客服系统</h1>
<h2>iframe+长连接,获取实时内容,返回到主界面</h2>
<div id="msgzone">
</div>
回复:<span id="rec"></span>
<textarea></textarea>
<input type="button" value="回复" onclick="huifu();">
<p><iframe src="AJ04serve.php" width="0" height="0" iframeBorder="0"></iframe></p>
</body>
</html>
<2>AJ04serve.php
<?php
//永久执行直到程序结束
set_time_limit(0);
//导入连接数据库的代码
require('./AJconn.php');
//客服端一直在监听的事件
while(true){
//从数据库中读取rec="admin" and isread=0'的客服的对话数据
$sql='select * from msg where rec="admin" and isread=0';
//使用mysql_query读取内容
$rs=mysql_query($sql,$conn);
//取得结果集的一行并放入数组中
$msg=mysql_fetch_assoc($rs);
if(empty($msg)){
}else{
//更新数据库中isread=0的行改为isread=1
$sql='update msg set isread=1 where mid='.$msg['mid'].' limit 1';
//使用mysql_query更新内容内容
mysql_query($sql,$conn);
//使用json_encode()将php数据封装在js中,以在js中访问
$msg=json_encode($msg);
echo '<script type="text/javascript">';
echo 'parent.window.comet('.$msg.');';
echo '</script>';
//强迫php把内容发给Apache
ob_flush();
//强迫apache发给浏览器
flush();
}
sleep(1);
}
?>
客户端:
<3>AJ05customer.php
(方式一)使用本地jQuery库
我使用的是本地的jquery.js,可以在地址栏里输入链接
//libs.baidu.com/jquery/1.7.2/jquery.min.js,复制内容保存为jquery.js
<script type="text/javascript" src="jquery.js"></script>
(方式二)使用远程jQuery链接库
<script type="text/javascript" src="//libs.baidu.com/jquery/1.7.2/jquery.min.js"></script>
<?php
//对于每一个新进入的用户进行编号
if(empty($_COOKIE['username'])){
setcookie('username','admin'.rand(100,999));
}
?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" lang="zh-CN">
<head>
<title>新建网页</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<script type="text/javascript" src="jquery.js"></script>
<script type="text/javascript">
function xunwen(){
//获取对话框中发送的数据
var cont=$('textarea:first').val();
//将数据提交到AJsendmsg.php,根据回调函数,将信息显示在屏幕上
$.post('AJsendmsg.php',{rec:'admin',content:cont},function(res){
if(res=='OK'){
$('<p>你对客服说<br />'+cont+'</p>').appendTo($('#msgzone'));
//将发送信息的文本框内的数据清空,体现发送数据的动态效果
$('textarea:first').val('');
}
});
}
</script>
<style type="text/css">
#msgzone{
border: 1px solid gray;
width:300px;
height:400px;
overflow:auto;
}
</style>
</head>
<body>
<h1>Ajax顾客系统</h1>
<h2>iframe+长连接+长轮询获取实时内容,返回到主界面</h2>
<div id="msgzone">
</div>
<textarea></textarea>
<input type="button" value="询问" onclick="xunwen();">
</body>
<script type="text/javascript">
//接受来自客服的信息
var setting={url:'AJ05comitajax.php',
dataType:'json',
success:function(res){
// 每隔一秒返回一次数据,并继续监听
window.setTimeout(function(){$.ajax(setting)},1000);
//将接受的数据显示在对话框中
$('<p>客服对你说<br/>'+res.content+'</p>').appendTo($('#msgzone'));
}
};
$.ajax(setting);
</script>
</html>
<4>AJ05comitajax.php
<?php
// 永久执行直到程序结束
set_time_limit(0);
//导入连接数据库的代码
require('./AJconn.php');
//从COOKIE中获取用户username
$rec=$_COOKIE['username'];
//遍历数据库接受者是用户的信息且isread=0
$sql="select * from msg where rec='$rec' and isread=0 limit 1";
//客户端一直在监听的事件
while(true){
//执行sql语句
$rs=mysql_query($sql,$conn);
//找出结果集符合条件的一条字段集
$row=mysql_fetch_assoc($rs);
if(!empty($row)){
//跟新数据库数据isread=1
$sql="update msg set isread=1 where mid=" . $row['mid'];
mysql_query($sql,$conn);
//对结果集进行json格式的编码,便于数据传输和利用
echo json_encode($row);
exit();
}
//减轻服务器负担
sleep(1);
}
?>
<5>.连接数据库:conn.php
<?php
//连接数据库
$conn=mysql_connect('localhost','root','');
mysql_query('use test',$conn);
mysql_query('set names utf8',$conn);
?>
<6>.将聊天信息插入数据库:AJsendmsg.php
<?php
//将消息插入数据库
require('./AJconn.php');
$rec=$_POST['rec'];
$content=$_POST['content'];
$pos=$_COOKIE['username'];
$sql="insert into msg(rec,pos,content) values('$rec','$pos','$content')";
echo mysql_query($sql,$conn)?'OK':'FIAL';
?>
》》》目录视图
》》》数据库准备
》》》效果展示