您的位置:首页 > 编程语言 > PHP开发

php+ajax长轮询实现web即时聊天

2017-01-10 21:23 585 查看
web im的实现方式有很多种:
1.普通轮询,原理通过js定时重复发送ajax请求服务端,获取数据后显示。
2. 长轮询,ajax请求服务端,服务端有数据会立即返回。服务端无数据时会一直等待,直到有数据了才立即返回。
3.socket长连接。

特征分析:
方法1:实现起来最容易,定时重复请求服务端会产生无意义的http连接,消耗服务端资源,实时性较差.
方法2:实现起来较容易,会减少无效的ajax请求产生的http连接,能即时返回数据,但服务端会一直挂着,会消耗一定的资源,处理并发能力不强,比较适合于中小型应用服务.(comet)
方法3:门槛较高,需了解socket通讯协议,是http实现长连接的最佳方式,也是真正意义上的server push技术.

Comet技术简介

  以即时通信为代表的web应用程序对数据的Low
Latency(低延时)要求,传统的基于轮询的方式已经无法满足,而且也会带来不好的用户体验。于是一种基于http长连接的“服务器推”技术便被hack出来。这种技术被命名为Comet,这个术语由Dojo
Toolkit 的项目主管Alex Russell在博文Comet: Low Latency Data for the Browser首次提出,并沿用下来。

  其实,服务器推很早就存在了,在经典的client/server模型中有广泛使用,只是浏览器太懒了,并没有对这种技术提供很好的支持。但是Ajax的出现使这种技术在浏览器上实现成为可能,
google的gmail和gtalk的整合首先使用了这种技术。随着一些关键问题的解决(比如 IE
的加载显示问题),很快这种技术得到了认可,目前已经有很多成熟的开源Comet框架。

  以下是典型的Ajax和Comet数据传输方式的对比,区别简单明了。典型的Ajax通信方式也是http协议的经典使用方式,要想取得数据,必须首先发送请求。在Low

Latency要求比较高的web应用中,只能增加服务器请求的频率。Comet则不同,客户端与服务器端保持一个长连接,只有客户端需要的数据更新时,服务器才主动将数据推送给客户端。

本文介绍第二种实现方法
案例名称:web即时聊天(ajax长轮询方式实现)
项目地址:https://github.com/zhangrenjie/web_im_ajax

功能介绍:

对话双方都在线(浏览器没有关闭的情况下),对话即时推送.

支持离线发送消息.当离线方上线时,会自动接收离线消息.

采用确认机制确保数据推送成功.

采用超时退出机制,降低服务器资源浪费.

~~本项目只注重php服务端的实现机制和性能优化,前端界面粗糙请忽略.适合中级php程序员学习借鉴,欢迎各位指教交流~~

预览





项目文件结构:
GetMessage.php
SendMessage.php
client.php
jquery.min.js
sql


准备工作:数据库
mysql> desc message;
+-------------+------------------+------+-----+---------+----------------+
| Field       | Type             | Null | Key | Default | Extra          |
+-------------+------------------+------+-----+---------+----------------+
| id          | int(10)          | NO   | PRI | NULL    | auto_increment |
| reciver_uid | int(10) unsigned | NO   | MUL | 0       |                |
| sender_uid  | int(10) unsigned | NO   |     | 0       |                |
| content     | varchar(1000)    | NO   |     |         |                |
| create_time | int(10) unsigned | NO   |     | 0       |                |
| status      | tinyint(1)       | NO   |     | 0       |                |
+-------------+------------------+------+-----+---------+----------------+


客户端Client.php
实现功能:1.发送聊天信息,2即时获取并显示聊天内容

页面基本结构
<div id="message-list">
<!--这里显示对话内容-->
</div>

<div id="message-send">
<!--这里填写对话内容,并发送-->
<input type="textarea" id="message-box"/>
<input type="button" id="submit-message" value="发送消息">
</div>


功能1:发送内容操作
<script type="text/javascript">
//-------------发送消息---------
$(function () {
var reciver_uid = <?php echo $reciverUid;?>;
var sender_uid = <?php echo $senderUid;?>;
$('#submit-message').on('click', function () {
var message_content = $('#message-box').val();
if (message_content != '') {
$(this).attr('disabled', 'disabled');
var send_url = './SendMessage.php';
var send_data = {
'message': message_content,
'reciver_uid': reciver_uid,
'sender_uid': sender_uid,
};
$.post(send_url, send_data, function (response) {
if (response.status == 1) {
$('#message-box').val('');
$('#submit-message').removeAttr('disabled');
var send_message_str = '<li style="text-align: right;padding-right: 10px;">';
send_message_str += '您对' + send_data.reciver_uid + '说:' + send_data.message;
send_message_str += '</li>';
$('#message-list').append(send_message_str);
} else {
console.log('发送失败!!');
}
}, 'json');

}
});
});
</script>


处理发生消息SendMessage.php
实现功能:保存发送信息

$link = mysqli_connect(
'127.0.0.1',  /* The host to connect to 连接MySQL地址 */
'root',      /* The user to connect as 连接MySQL用户名 */
'',         /* The password to use 连接MySQL密码 */
'web_im');    /* The default database to query 连接数据库名称*/
if (!$link) {
printf("Can't connect to MySQL Server. Errorcode: %s ", mysqli_connect_error());
exit;
}
//只能用函数来判断是否连接成功
if (mysqli_connect_errno()) {
echo mysqli_connect_error();
}

$senderUid = (int)$_POST['sender_uid'];
$reciverUid = (int)$_POST['reciver_uid'];
$message = str_replace([' ', ','], '', $_POST['message']);
$time = time();

$sql = "insert into message values(NULL ,'{$reciverUid}','{$senderUid}','{$message}','{$time}','1')";
$result = mysqli_query($link, $sql);
$insertId = mysqli_insert_id($link);
if ($insertId) {
$returnArr = ['status' => 1,'info' => $insertId,];
} else {
$returnArr = ['status' => 0,'info' => '',];
}
echo json_encode($returnArr);
mysqli_close($link);
exit();


再回到客户端Client.php的功能2
功能2:即时获取并显示聊天内容(注意:客户端使用了递归跟服务端自动应答)
<script type="text/javascript">
var reciver_uid = <?php echo $senderUid;?>;
var sender_uid = <?php echo $reciverUid;?>;
var url = './GetMessage.php';
$(function () {
get_message_reply(url, reciver_uid, sender_uid, 'get_message', '');
});

//获取消息并应答
//get_get_message_reply()
//param request_type  请求类型 详解:
//      get_message   获取信息
//      comfrim_read  确认已经读取了信息
function get_message_reply(url, reciver_uid, sender_uid, request_type, send_data) {
var setting = {
url: url,
data: {
'request_type': request_type,
'reciver_uid': reciver_uid,
'sender_uid': sender_uid,
'send_data': send_data,
},
type: 'post',
dataType: 'json',
success: function (response) {
if (response.status == 1) {
if (response.response_type == 'is_read') {
//将消息写入到消息盒子
var messages = response.info;
var message_str = '';
var id_arr = new Array();
for (var i in messages) {
id_arr.push(messages[i]['id']);
message_str += '<li>' + messages[i]['sender_uid'] + '在' + messages[i]['send_time'] + '的时候对您说:' + messages[i]['content'] + '</li>';
}
$('#message-list').append(message_str);
//确认收到消息
get_message_reply(url, reciver_uid, sender_uid, 'comfrim_read', id_arr);

} else if (response.response_type == 'is_connecting') {
//继续获取消息
get_message_reply(url, reciver_uid, sender_uid, 'get_message', '');
}
}
}
};
$.ajax(setting);
}
</script>


NOTICE:下面是核心中的核心

服务端推送消息GetMessage.php
set_time_limit(0);
$maxInvalidCount = 30;
$link = mysqli_connect(
'127.0.0.1',  /* The host to connect to 连接MySQL地址 */
'root',      /* The user to connect as 连接MySQL用户名 */
'',         /* The password to use 连接MySQL密码 */
'web_im');    /* The default database to query 连接数据库名称*/
if (!$link) {
printf("Can't connect to MySQL Server. Errorcode: %s ", mysqli_connect_error());
exit;
}
//只能用函数来判断是否连接成功
if (mysqli_connect_errno()) {
echo mysqli_connect_error();
}

$requestType = $_POST['request_type'];
switch ($requestType) {
case 'get_message'://客户端请求读取消息
break;
case 'comfrim_read'://客户端确认已经读取了信息,服务端需要更新读取状态
$idsArr = $_POST['send_data'];
$ids = implode(',', $idsArr);
$sql = "update message set status = 2 where id in ({$ids})";
mysqli_query($link, $sql);
mysqli_close($link);
break;
default:
break;
}

$sql = "select * from message where reciver_uid='{$_POST['reciver_uid']}' and sender_uid='{$_POST['sender_uid']}' and status='1'";
$i = 0;
while (true) {
//读取数据
$result = mysqli_query($link, $sql);
if ($result) {
$returnArr = [];
while ($row = mysqli_fetch_assoc($result)) {
$row['send_time'] = date('Y-m-d H:i:s', $row['create_time']);
$returnArr[] = $row;
}
if (!empty($returnArr)) {
//返回结果
$data = [
'status' => 1,
'response_type' => 'is_read',
'info' => $returnArr,
];
echo json_encode($data);
mysqli_free_result($result);
mysqli_close($link);
exit();
}
}

$i++;
//需要给客户端发送确认信息是否还在连接服务器,客户端无回应则整个过程结束
if ($i == $maxInvalidCount) {
$data = [
'status' => 1,
'response_type' => 'is_connecting',
'info' => '',
];
echo json_encode($data);
mysqli_close($link);
exit();
}
//file_put_contents('./test.log', date('Y-m-d H:i:s') . "已经重试{$i}次没有获取到信息" . "\r\n", FILE_APPEND);
sleep(1);
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息