web 实时通信的方法总结

一、概述

1.Web端即时通讯技术

即时通讯技术简单的说就是实现这样一种功能:服务器端可以即时地将数据的更新或变化反应到客户端,例如消息即时推送等功能都是通过这种技术实现的。但是在Web中,由于浏览器的限制,实现即时通讯需要借助一些方法。这种限制出现的主要原因是,一般的Web通信都是浏览器先发送请求到服务器,服务器再进行响应完成数据的现实更新。

2.实现Web端即时通讯的方法

实现即时通讯主要有四种方式,它们分别是短轮询、长轮询(comet)、长连接(SSE)、WebSocket。它们大体可以分为两类,一种是在HTTP基础上实现的,包括短轮询、comet和SSE;另一种不是在HTTP基础上实现是,即WebSocket。下面分别介绍一下这四种轮询方式,以及它们各自的优缺点。

(1)短轮询

短轮询的基本思路就是浏览器每隔一段时间向浏览器发送http请求,服务器端在收到请求后,不论是否有数据更新,都直接进行响应。这种方式实现的即时通信,本质上还是浏览器发送请求,服务器接受请求的一个过程,通过让客户端不断的进行请求,使得客户端能够模拟实时地收到服务器端的数据的变化。

这种方式的优点是比较简单,易于理解,实现起来也没有什么技术难点。缺点是显而易见的,这种方式由于需要不断的建立http连接,严重浪费了服务器端和客户端的资源。尤其是在客户端,距离来说,如果有数量级想对比较大的人同时位于基于短轮询的应用中,那么每一个用户的客户端都会疯狂的向服务器端发送http请求,而且不会间断。人数越多,服务器端压力越大,这是很不合理的。

因此短轮询不适用于那些同时在线用户数量比较大,并且很注重性能的Web应用。

(2)comet

comet指的是,当服务器收到客户端发来的请求后,不会直接进行响应,而是先将这个请求挂起,然后判断服务器端数据是否有更新。如果有更新,则进行响应,如果一直没有数据,则到达一定的时间限制(服务器端设置)后关闭连接。

长轮询和短轮询比起来,明显减少了很多不必要的http请求次数,相比之下节约了资源。长轮询的缺点在于,连接挂起也会导致资源的浪费。

(3)SSE

SSE是HTML5新增的功能,全称为Server-SentEvents。它可以允许服务推送数据到客户端。SSE在本质上就与之前的长轮询、短轮询不同,虽然都是基于http协议的,但是轮询需要客户端先发送请求。而SSE最大的特点就是不需要客户端发送请求,可以实现只要服务器端数据有更新,就可以马上发送到客户端。

SSE的优势很明显,它不需要建立或保持大量的客户端发往服务器端的请求,节约了很多资源,提升应用性能。并且后面会介绍道,SSE的实现非常简单,并且不需要依赖其他插件。

(4)WebSocket

WebSocket是HTML5定义的一个新协议,与传统的http协议不同,该协议可以实现服务器与客户端之间全双工通信。简单来说,首先需要在客户端和服务器端建立起一个连接,这部分需要http。连接一旦建立,客户端和服务器端就处于平等的地位,可以相互发送数据,不存在请求和响应的区别。

WebSocket的优点是实现了双向通信,缺点是服务器端的逻辑非常复杂。现在针对不同的后台语言有不同的插件可以使用。

3.四种Web即时通信技术比较

从兼容性角度考虑,短轮询>长轮询>长连接SSE>WebSocket;

从性能方面考虑,WebSocket>长连接SSE>长轮询>短轮询

二、短轮询

1.原理

短轮询利用http的请求/响应模式,通过每隔一定时间向服务器发送请求来获得服务器数据的更新。在开发过程中,可以利用ajax,然后通过setInterval()方法,实现每隔一段时间向服务器发送请求的功能。

2.实现

后端以php为例进行实现。

客户端代码:

<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>短轮询ajax实现</title>
<script type="text/javascript" src="../jquery.min.js"></script>
</head>
<body>
    <form id="form1" runat="server">
        <div id="news"></div>
    </form>
</body>
<script type="text/javascript">
    function showUnreadNews(){
            $(document).ready(function() {
//使用jquery的ajax方法
                $.ajax({
                  type: "GET",
                  url: “do.php",  //请求发送到的后台php文件
                  dataType: "json",
       //请求成功的回调方法,遍历获取到的数据进行客户度界面的更新
                  success: function(msg) {
                      $.each(msg, function(id, title) {
                          $("#news").append("<a>" + title + "</a><br>");
                      });
                  }
               });
            });
        }
 //设置每2s执行一次showUnreadNews()方法,向服务器发送请求
        setInterval('showUnreadNews()',2000);
</script>
</html>

服务器端代码(php):

<?php
$arr = array('title'=>'新闻','text'=>'新闻内容');   //服务器端更新的数据
echo json_encode($arr);  //返回的öson数据
?>

三、comet

1.原理

comet与传统的ajax区别在于,客户端会与服务器端保持一个长连接,只有当客户端需要的数据更新时,服务器才会主动将数据推送给客户端。comet的实现有两种方式,一种方式是使用基于ajax的长轮询,另一种方式是基于Iframe及htmlfile的流方式。基于ajax的长轮询方式中,服务器端在接受到客户端ajax发送的请求后,不立即返回响应,而是阻塞请求直到超时或有数据更新。当服务器端在上述情况下返回响应后,客户端通过js再次发送请求建立连接,重复上述步骤。

2.实现

客户端代码:

<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>长轮询ajax实现</title>
<script type="text/javascript" src="../jquery.min.js"></script>
</head>
<body>
<input type="button" id="btn" value="click">
<div id="msg">数据情况</div>
</body>
<script type="text/javascript">
$(function(){
        $("#btn").bind('click',{btn:$('#btn')},function(e){
            $.ajax({
                type: 'POST',
                dataType: 'json',               
                url: 'ajax.php',
                timeout: '20000',//设置请求超时时间
                data: {time: '2000000'},// 每次请求等待时间,将其传到后台用来挂起请求
                success: function(data,status){
//对返回的数据进行判断和读取
                    if(data.success == '1'){
                    $("#msg").append('<br>[有数据]'+data.text);
                    e.data.btn.click();  //再此发送请求
                }
                    // 未从服务器中获的数据
                      if(data.success == '0'){
                        $("#msg").append('<br>[无数据]');
                            e.data.btn.click();
                    }
                },
                // ajax超时,继续查询
                error:function(XMLHttpRequest,textStatus,errorThrown){
                     if(textStatus == "timeout"){
                        $("#msg").append('超时');
                        e.data.btn.click();
                     }
                }
          });
        });
    });
</script>
</html>
服务器端代码(php):
<?php
  set_time_limit(0);// 无限请求超时时间
  usleep($_POST['time’]);//通过前台传过来的数据设置挂起时间
  while(true){   //无限循环
      $rand = rand(1,999);
      if($rand < 500){
          $arr = array('success'=>'1','name'=>'有值','text'=>$rand);
          echo json_encode($arr);
          exit();
      }else{
          $arr = array('success'=>'0','name'=>'无值','text'=>$rand);
          echo json_encode($arr);
          exit();
      }
   }
?>

四、SSE

1.原理

SSE不需要依赖客户端向服务器发送请求,而是可以直接在服务器端有数据更新时进行发送到客户端,相比于轮询的“拉数据”,这种“推数据” 有着低延迟、高性能的优势。

这种方法的服务器端非常简介,只要维护一个服务器和客户端之间的协议即可。前端使用EventSource对象。

服务器端需要提供的协议基本代码如下:

data:firstevent

data:secondevent

id:100

event:myevent

data:thirdevent

id:101

:thisisacomment

data:fourthevent

data:fourtheventcontinue

下面解释一下基本用法。要定义各个事件,每一个事件之间使用一个换行符隔开。每个事件内部可以有多行,每一行都是type:value的形式。type有以下集中选择:

(1)类型为空白,表示该行是注释,会在处理时被忽略。

(2)类型为data,表示该行包含的是数据。以data开头的行可以出现多次。所有这些行都是该事件的数据。

(3)类型为event,表示该行用来声明事件的类型。浏览器在收到数据时,会产生对应类型的事件。

(4)类型为id,表示该行用来声明事件的标识符。

(5)类型为retry,表示该行用来声明浏览器在连接断开之后进行再次连接之前的等待时间。

比如上面的第一个事件,只传输了一个数据,数据内容为firstevent。服务器端通过这个清单发送到客户端,就可以通过前端进行响应的处理,诸如读取新数据、更新界面等。

客户端需要在JavaScript中使用EventSource对象。

首先需要初始化一个EventSource对象,实例化的时候需要传入与其交互的服务器端的文件地址,如:

vares=newEventSource(“sse.php”);

接下来,可以对进行事件的监听。EventSource给出了三种标准事件,它们的名称和触发时机如下表:

open 当成功与服务器建立连接时执行

message 当收到服务器发送的事件时执行

error 当出现错误时执行

和普通的事件一样,可以通过以下两种方法使用这些事件:

es.onmessage=function(e){};

es.addEventListener(“message”,function(e){});

2.实现

服务器端代码(php):

<?php
header('Content-Type: text/event-stream'); //这是专门为sse设置的数据格式
$time = date('Y-m-d H:i:s');
//下面这些echo出来的东西就是上面说的服务器端和客户端之间的协议
echo 'retry: 3000'.PHP_EOL; //retry类型的数据,规定了浏览器在连接断开之后进行再次连接之前的等待时间
echo 'data: The server time is: '.$time.PHP_EOL.PHP_EOL;
?>
注意必须要先设定content-type为text/event-stream,这是为SSE专门定义的数据传输格式。

接下来通过php的echo输出协议,上面的代码输出的结果如下:

retry:3000

data:Theservertimeis...

输出了一个事件,这个事件中分别定义了retry类型和data类型的行。

客户端代码:

<html>
    <head>
        <meta charset="UTF-8">
        <title>basic SSE test</title>
    </head>
    <body>
        <div id=”content”></div>
    </body>
    <script>
        var es = new EventSource("sse.php");
        es.addEventListener("message",function(e){
            document.getElementById("content").innerHTML += "\n"+e.data;
        });
    </script>
</html>

上面的代码首先实例化了一个EventSource对象,并传入与之通信的服务器端文件sse.php。利用addEventListener()方法,为对象绑定一个message事件(上面提到message在收到服务器发送的事件时执行)。当客户端收到服务器传来的协议时,为页面中添加数据,通过e.data获取,对应了服务器端文件中协议表的data类型定义的数据,即Theservertimeis...。

五、WebSocket

1.原理

WebSocket的实现了一次连接,双方通信的功能。首先由客户端发出WebSocket请求,服务器端进行响应,实现类似TCP握手的动作。这个连接一旦建立起来,就保持在客户端和服务器之间,两者之间可以直接的进行数据的互相传送。服务器端的逻辑比较复杂,如果是java或者node开发,都有很多封装好的组件可以使用。

2.前端API

(1)创建WebSocket对象

varws=newWebSocket(“ws//localhost:8080”);

WebSocket是一个不同于HTTP的协议,其参数传递中的ws://前缀类似于http://,用于进行协议的声明。

(2)事件操作

WebSocket提供了四个事件操作,如下表:

onmessage收到服务器响应时执行

onerroe 出现异常时执行

onopen 建立起连接时执行

onclose 断开连接时执行

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,233评论 4 360
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,013评论 1 291
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,030评论 0 241
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,827评论 0 204
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,221评论 3 286
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,542评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,814评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,513评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,225评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,497评论 2 244
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,998评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,342评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,986评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,055评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,812评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,560评论 2 271
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,461评论 2 266

推荐阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,099评论 18 139
  • 最年长的作者:马风 马风老师出生于1936年,退休前是一位编剧,退休后依然热爱写作,发现简书这个写作极其便利的产品...
    简书阅读 13,431评论 121 431
  • 01画彩铅的第一步,都要先画出线框图,此时不必过于纠结,只要画出大概的轮廓即可,后面还可以进行不断的修改。 02....
    墨竹_sunshine阅读 2,736评论 7 9
  • 候车的时候,我问了我的一个同事,说:最近有看什么书吗? 他说,最近重看了《社会契约论》的第一卷。然后就说了他重看后...
    力牧阅读 312评论 0 3