博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
php+ajax长轮询实现web即时聊天
阅读量:6843 次
发布时间:2019-06-26

本文共 7742 字,大约阅读时间需要 25 分钟。

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在博文首次提出,并沿用下来。

其实,服务器推很早就存在了,在经典的client/server模型中有广泛使用,只是浏览器太懒了,并没有对这种技术提供很好的支持。但是Ajax的出现使这种技术在浏览器上实现成为可能, google的gmail和gtalk的整合首先使用了这种技术。随着一些关键问题的解决(比如 IE 的加载显示问题),很快这种技术得到了认可,目前已经有很多成熟的开源Comet框架。
以下是典型的Ajax和Comet数据传输方式的对比,区别简单明了。典型的Ajax通信方式也是http协议的经典使用方式,要想取得数据,必须首先发送请求。在Low Latency要求比较高的web应用中,只能增加服务器请求的频率。Comet则不同,客户端与服务器端保持一个长连接,只有客户端需要的数据更新时,服务器才主动将数据推送给客户端。

本文介绍第二种实现方法

案例名称:web即时聊天(ajax长轮询方式实现)

项目地址:

功能介绍: 

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

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

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

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

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

预览

项目文件结构:

1
2
3
4
5
GetMessage.php
SendMessage.php
client.php
jquery.
min
.js
sql

准备工作:数据库

1
2
3
4
5
6
7
8
9
10
11
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即时获取并显示聊天内容

页面基本结构

1
2
3
4
5
6
7
8
9
<
div 
id
=
"message-list"
>
<!--这里显示对话内容-->
</
div
>
 
<
div 
id
=
"message-send"
>
<!--这里填写对话内容,并发送-->
    
<
input 
type
=
"textarea" 
id
=
"message-box"
/>
    
<
input 
type
=
"button" 
id
=
"submit-message" 
value
=
"发送消息"
>
</
div
>

功能1:发送内容操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
<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

实现功能:保存发送信息

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
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();    
}  
   
$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:即时获取并显示聊天内容(注意:客户端使用了递归跟服务端自动应答)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
<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 
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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
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);    
}
本文转自 hgditren 51CTO博客,原文链接:http://blog.51cto.com/phpme/1890859,如需转载请自行联系原作者
你可能感兴趣的文章
Flask最佳实践
查看>>
kafka源码剖析(三)之日志管理-LogManager
查看>>
tcp连接wait连接过多解决
查看>>
EVE模拟器教程之如何设置预配
查看>>
Dell PowerEdge R720xd 上的 Microsoft Exchange 2010 与 R510 上的 Exchange 2007 之间的跨代大PK...
查看>>
A记录、NS记录、CNAME记录的区别和联系
查看>>
nginx搭建支持http和rtmp协议的流媒体服务器之---环境搭建
查看>>
Java 内存分配全面浅析
查看>>
Linux 文件与权限管理
查看>>
2016-12-7 第一次 测试
查看>>
ansible 安装与基本功能的使用
查看>>
AIX引导级别及级别修改
查看>>
获得第一批用户
查看>>
2月第一周网络安全报告:放马站点域名315个
查看>>
大数据,TB、PB、EB,你了解多少?
查看>>
CSS3中的弹性流体盒模型技术详解(一)
查看>>
linux禁用休眠
查看>>
RH413-RHEL6.4课程总结
查看>>
MaxCompute(ODPS)上处理非结构化数据的Best Practice
查看>>
想了解产品经理吗?---经典文章
查看>>