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

本文涉及的产品
云数据库 RDS MySQL,集群系列 2核4GB
推荐场景:
搭建个人博客
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
云数据库 RDS MySQL,高可用系列 2核4GB
简介:

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

功能介绍: 

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

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

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

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



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


预览


wKiom1h1uG-zsJ0JAADVtwsyXBc807.png-wh_50



项目文件结构:

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,如需转载请自行联系原作者
相关实践学习
如何在云端创建MySQL数据库
开始实验后,系统会自动创建一台自建MySQL的 源数据库 ECS 实例和一台 目标数据库 RDS。
全面了解阿里云能为你做什么
阿里云在全球各地部署高效节能的绿色数据中心,利用清洁计算为万物互联的新世界提供源源不断的能源动力,目前开服的区域包括中国(华北、华东、华南、香港)、新加坡、美国(美东、美西)、欧洲、中东、澳大利亚、日本。目前阿里云的产品涵盖弹性计算、数据库、存储与CDN、分析与搜索、云通信、网络、管理与监控、应用服务、互联网中间件、移动服务、视频服务等。通过本课程,来了解阿里云能够为你的业务带来哪些帮助 &nbsp; &nbsp; 相关的阿里云产品:云服务器ECS 云服务器 ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新。产品详情: https://www.aliyun.com/product/ecs
目录
相关文章
|
10天前
|
XML 前端开发 JavaScript
34 PHP与Ajax
路老师在知乎上分享了关于PHP语言的知识,帮助大家入门并深入了解PHP。本文重点介绍了Ajax技术,包括其概念、开发模式、优点及常用技术,如JavaScript和XMLHttpRequest对象。Ajax通过异步请求改善了用户体验,减轻了服务器负担,实现了页面无刷新更新。
24 1
|
15天前
|
前端开发 API 开发者
Python Web开发者必看!AJAX、Fetch API实战技巧,让前后端交互如丝般顺滑!
在Web开发中,前后端的高效交互是提升用户体验的关键。本文通过一个基于Flask框架的博客系统实战案例,详细介绍了如何使用AJAX和Fetch API实现不刷新页面查看评论的功能。从后端路由设置到前端请求处理,全面展示了这两种技术的应用技巧,帮助Python Web开发者提升项目质量和开发效率。
30 1
|
17天前
|
XML 安全 PHP
PHP与SOAP Web服务开发:基础与进阶教程
本文介绍了PHP与SOAP Web服务的基础和进阶知识,涵盖SOAP的基本概念、PHP中的SoapServer和SoapClient类的使用方法,以及服务端和客户端的开发示例。此外,还探讨了安全性、性能优化等高级主题,帮助开发者掌握更高效的Web服务开发技巧。
|
29天前
|
开发框架 自然语言处理 PHP
PHP在Web开发中的持久魅力与创新实践###
【10月更文挑战第17天】 本文探讨了PHP作为一门老牌却充满活力的编程语言,在现代Web开发中的独特优势和未来趋势。通过分析其简洁性、灵活性、强大生态系统及不断创新的特性,本文旨在揭示PHP为何能持续吸引开发者,并在技术快速迭代的时代保持竞争力。同时,文章也展望了PHP在未来Web开发领域的发展潜力,强调其在技术创新和社区支持下,依然能够引领Web开发的新潮流。 ###
38 9
|
23天前
|
SQL 安全 Go
PHP在Web开发中的安全实践与防范措施###
【10月更文挑战第22天】 本文深入探讨了PHP在Web开发中面临的主要安全挑战,包括SQL注入、XSS攻击、CSRF攻击及文件包含漏洞等,并详细阐述了针对这些风险的有效防范策略。通过具体案例分析,揭示了安全编码的重要性,以及如何结合PHP特性与最佳实践来加固Web应用的安全性。全文旨在为开发者提供实用的安全指南,帮助构建更加安全可靠的PHP Web应用。 ###
33 1
|
1月前
|
安全 编译器 API
探索PHP 8的新特性及其对现代Web开发的影响
【10月更文挑战第5天】随着PHP 8的发布,这门历史悠久的脚本语言重获新生。PHP 8引入了联合类型、命名参数、属性、空安全运算符及JIT编译器等一系列新特性,不仅提升了开发者的编程体验,还增强了PHP在现代Web开发领域的竞争力。本文将详细介绍这些新特性及其对Web开发的影响。例如,联合类型允许函数参数接受多种类型,提高代码灵活性;命名参数则使函数调用更加直观易懂;属性可用于装饰类、方法等,提供额外信息;空安全运算符避免了访问未定义属性时的错误;JIT编译器则显著提升了性能。这些改进共同提升了代码质量和开发效率,巩固了PHP在Web开发中的地位。
22 4
|
21天前
|
关系型数据库 API PHP
PHP在Web开发中的优势与实践###
【10月更文挑战第24天】 PHP是一种流行的服务器端脚本语言,特别适合Web开发。其简单易学、灵活性高和广泛应用的特点,使其成为众多开发者的首选。本文将探讨PHP在Web开发中的主要优势及其实际应用,通过实例展示如何使用PHP构建高效、可靠的Web应用。无论你是初学者还是有经验的开发者,这篇文章都将提供有价值的见解和实用技巧。 ###
27 0
|
1月前
|
前端开发 JavaScript API
惊呆了!学会AJAX与Fetch API,你的Python Web项目瞬间高大上!
在Web开发领域,AJAX与Fetch API是提升交互体验的关键技术。AJAX(Asynchronous JavaScript and XML)作为异步通信的先驱,通过XMLHttpRequest对象实现了局部页面更新,提升了应用流畅度。Fetch API则以更现代、简洁的方式处理HTTP请求,基于Promises提供了丰富的功能。当与Python Web框架(如Django、Flask)结合时,这两者能显著增强应用的响应速度和用户体验,使项目更加高效、高大上。
50 2
|
1月前
|
前端开发 API 开发者
从零到精通,AJAX与Fetch API让你的Python Web前后端交互无所不能!
从零到精通,AJAX与Fetch API让你的Python Web前后端交互无所不能!
43 3
ly~
|
2月前
|
存储 监控 小程序
除了 Web 开发,PHP 还可以应用于哪些领域?
PHP 在 Web 开发之外还有多个应用场景:1)命令行脚本,如批量处理文件、数据库管理及系统监控;2)利用 PHP-GTK 等工具开发桌面应用,满足特定业务需求;3)结合微信云开发功能支持微信小程序后端,处理数据存储与用户认证;4)为小型游戏或特定类型游戏开发游戏服务器逻辑;5)在物联网领域作为后端语言处理设备数据交互与分析。
ly~
46 4