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

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程序员学习借鉴,欢迎各位指教交流~~


预览




项目文件结构:

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
"text/javascript" >
     //-------------发送消息---------
     $( function  () {
         var  reciver_uid = ;
         var  sender_uid = ;
         $( '#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 =  '' ;
                         send_message_str +=  '您对'  + send_data.reciver_uid +  '说:'  + send_data.message;
                         send_message_str +=  '' ;
                         $( '#message-list' ).append(send_message_str);
                     else  {
                         console.log( '发送失败!!' );
                     }
                 },  'json' );
 
             }
         });
     });



处理发生消息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
"text/javascript" >
     var  reciver_uid = ;
     var  sender_uid = ;
     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 +=  '
  • '  + messages[i][ 'sender_uid' ] +  '在'  + messages[i][ 'send_time' ] +  '的时候对您说:'  + messages[i][ 'content' ] +  '
  • ' ;
                             }
                             $( '#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);
         }


    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,如需转载请自行联系原作者

    你可能感兴趣的:(php+ajax长轮询实现web即时聊天)