一、概述
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 斷開連接時執行