网页实时聊天之js和jQuery实现ajax长轮询

众所周知,HTTP协议是无状态的,所以一次的请求都是一个单独的事件,和前后都没有联系。所以我们在解决网页实时聊天时就遇到一个问题,如何保证与服务器的长时间联系,从而源源不段地获取信息。

一直以来的方式无非有这么几种:

1、长连接,即服务器端不断开联系,PHP服务器端用ob系列函数来不停的读取输出,但是相当耗费服务器资源。

2、Flash socket,flash的as3语言,创建一个socket服务器用来处理信息。

3、轮询,顾名思义就是不停地发送查询消息,一有新消息立刻更新,但是会有多次无用请求。

4、长轮询,是轮询的升级版,需要服务器端的配合。

5、websocket,HTML5的通信功能,建立一个与服务器端的专用接口ws协议来进行通讯,兼容可能成为问题,改天研究一下这个。

这篇博文总结一下用JS和JQ两种方式(其实不同就是js和jq的实现),实现AJAX长轮询。

长轮询的思想:

如图:用AJAX发送询问信息,服务器在没有信息要返回的时候进入无限等待。由于AJAX异步的特性,PHP在服务器端执行等待不会影响到页面的正常处理。一旦服务器查询到返回信息,服务器返回信息,AJAX用回调函数处理这条信息,同时迅速再次发送一个请求等待服务器处理。

与传统轮询相比,长轮询在服务器没的返回信息的时候进入等待,减少了普通轮询服务器无数次的空回复。可以这样认为,长轮询使服务器每次的返回更有目的性,而不是盲目返回。

长轮询的服务器端实现:

聊天信息存储:

数据库设计为信息ID(msgid),发送人(sender),接收人(receiver),信息内容(content),设置senderRead和receiverRead的目的是标记信息是否已被读取,读取后改变标记,以区别信息是否已经被读取。

create table msg{

  msgid int not null primary key auto_increment,

  sender char(16) not null,

  receiver char(16) not null,

  content text,     //信息内容用text类型,存储量可达到65535字符

  senderRead tinyint enum(0,1) default 0,

  receiverRead tinyint enum(0,1) default 0    //设置一个是否已读的flag标记

}

PHP脚本:

 脚本的主要目的是处理来自ajax的每次询问,ajax每次询问就查询一下数据库,看有没有新的信息,如果没有,刚用usleep()函数等待一秒后再次查询,直到有新信息插入数据库并被查到,脚本返回查询到的数据,并退出无限循环,结束脚本。

  set_time_limit(0);//设置脚本超时时间为无限,不然在过了超时时间后脚本会自动关闭,轮询失败。

  $link=new mysqli("host","user","password","database");

  $search="select sender,receiver,content from msg where receiverRead=0 limit 1";//限制每次读出一条数据,便于修改其已读flag

  $change="update chat set receiverRead=1 where receiverRead=0 limit 1";

  while (true) {    //进入无限循环

      $res=$link->query($sql);  //查询结果

        if($res->num_rows!=0){  //当有未读信息时读取信息

            $link->query($change);//将信息的已读flag设为1

            $msg=$res->fetch_assoc();

            $jsonstr=json_encode($msg);//取到信息,将信息用转码为json格式,返回给JS

            echo $jsonstr;

            break;//输出信息后退出while循环,结束当前脚本

        }

      usleep(1000);//如果没有信息不会进入if块,但会执行一下等待1秒,防止PHP因循环假死。

  }

客户端实现:

客户端的主要任务是设置一个ajax请求函数,每次查询时被调用,当没有信息返回时,服务器端被搁置,当前页面正常执行;当有信息返回时,函数处理返回的数据,并迅速再次调用此函数发送一次请求。

用原生JS:

function link(){

    var xhr=null;//先设置xhr为空,为了轮询时再次调用函数对xhr重用,引发错误

    xhr=new XMLHttpRequest();

    xhr.open('GET','serviceback.php',true);//第三个参数一定要设置为true,异步不阻塞,不会影响到后面JS的执行。

    xhr.send();

    xhr.onreadystatechange=function(){

        if (xhr.readyState==4) { 严密也可加使用(xhr.readyState==4 && xhr.status ==200)限定服务器响应码为200时才进行处理。

              if(xhr.responseText!=''){

                   process...  //服务器端返回信息,且返回信息不为空,则开始处理返回信息。

             }

          setTimeout("link()",300);//递归再次调用link()函数,用setTimeOut()设置延时是因为服务器端进行sql操作时会耗时,当有新信息时,在服务器将要置已读flag为1还未成功时,AJAX可能已经又发出多条查询信息了,会导致一条信息多次返回。

        }

    };
}

用jQuery插件实现:

var link={            //jQuery的AJAX执行的配置对象

      type:"GET",      //设置请求方式,默认为GET,

      async:true,      //设置是否异步,默认为异步

      url:"customback.php",

      dataType:"json",    //设置期望的返回格式,因服务器返回json格式,这里将数据作为json格式对待

      success:function (msg){

            process...

            setTimeout("link()",300);

      }              //成功时的回调函数,处理返回数据,并且延时建立新的请求连接

}

$.ajax(link);          //执行ajax请求。

程序扩充:

添加发送聊天窗口:

新建一个函数用来处理ajax的POST请求,用ajax将发信人,每次发送的信息,收信人发送到服务器端,并设置一个单独的PHP脚本处理信息,将信息插入数据库。

需要注意的是,用JS原生实现POST请求发送信息时,要设置ajax对象的HTTP头,模拟表单提交的操作:

xhr.setRequestHeader("Content-type","application/x-www-form-urlencoded");

聊天室消息处理:

为了防止每次都查询到全部信息,我们对数据库的查询操作更改一下,设置idflag=0,每次查询后,设置idflag为查询到的数据的id,查询时我们查询比idflag大的ID,即,新添加进去的信息。

这样,一个简单的聊天室程序就做好了。

如果您觉得本文对您有帮助,您可以推荐或关注我,如果您有什么问题,可以在下方留言讨论,谢谢。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏大内老A

ASP.NET Core应用的错误处理[1]:三种呈现错误页面的方式

由于ASP.NET Core应用是一个同时处理多个请求的服务器应用,所以在处理某个请求过程中抛出的异常并不会导致整个应用的终止。出于安全方面的考量,为了避免敏感...

2568
来自专栏Java Edge

Netty 源码阅读入门实战(二)-Netty 基本组件1 一个简单的socket例子2 Netty对于socket的抽象3 Netty组件简单介绍

传统的HTTP服务器的原理创建一个ServerSocket

963
来自专栏Golang语言社区

几种服务器端IO模型的简单介绍及实现(上)

一些概念: 同步和异步 同步和异步是针对应用程序和内核的交互而言的,同步指的是用户进程触发I/O操作并等待或者轮询的去查看I/O操作是否就绪,而异步是指用户进程...

3447
来自专栏Java编程技术

Dubbo剖析-粘包与半包问题(一)

在客户端与服务端进行通信时候都会约定一个通讯协议,协议一般包含一个header和body,一个header和body组成了一次通讯的内容,一个通讯包。正常情况下...

882
来自专栏撸码那些事

【眼见为实】数据库并发问题 封锁协议 隔离级别

此篇博客是【眼见为实】系列的第一篇博客,主要从理论上讲了数据库并发可能会出现的问题,解决并发问题的技术——封锁,封锁约定的规则——封锁协议。然后简单说明了数据库...

27211
来自专栏PHP在线

Socket 通信原理

什么是Socket? Socket的中文翻译过来就是“套接字”。套接字是什么,我们先来看看它的英文含义:插座。 Socket就像一个电话插座,负责连通两端的电话...

4546
来自专栏前端知识分享

Vue---父子组件之间的通信

  在vue组件通信中其中最常见通信方式就是父子组件之中的通信,而父子组件的设定方式在不同情况下又各有不同。最常见的就是父组件为控制组件子组件为视图组件。父组件...

602
来自专栏Java架构沉思录

Java面试通关宝典(一)

前言 年后是跳槽的高峰期,相信有不少朋友已经跃跃欲试了。想要找到好工作,离不开充分的面试准备。刚好沉思君最近在网上看到一份Java面试知识点,觉得很不错,所以...

3145
来自专栏前端杂货铺

服务端事件EventSource揭秘

服务端推 服务端推,指的是由服务器主动的向客户端发送消息(响应)。在应用层的HTTP协议实现中,“请求-响应”是一个round trip,它的起点来自客户端,因...

2755
来自专栏我的博客

WorkerMan手册笔记二-TcpConnection类说明

一、介绍 每个客户端连接对应一个Connection对象,可以设置对象的onMessage、onClose等回调,同时提供了向客户端发送数据send接口与关闭连...

2876

扫码关注云+社区