大家好,又見面了,我是你們的朋友全棧君。
WebSocket詳解
- 1、什麼是Socket?什麼是WebSocket?
- 2、WebSocket的通信原理和機制
- 3、WebSocket技術出現之前,Web端實作即時通訊的方法有哪些?
- 4、一個簡單的WebSocket聊天小例子
- 5、結語
1、什麼是Socket?什麼是WebSocket?
對于第1次聽說WebSocket技術的人來說,兩者有什麼差別?websocket是僅僅将socket的概念移植到浏覽器中的實作嗎?
我們知道,在網絡中的兩個應用程式(程序)需要全雙工互相通信(全雙工即雙方可同時向對方發送消息),需要用到的就是socket,它能夠提供端對端通信,對于程式員來講,他隻需要在某個應用程式的一端(暫且稱之為用戶端)建立一個socket執行個體并且提供它所要連接配接一端(暫且稱之為服務端)的IP位址和端口,而另外一端(服務端)建立另一個socket并綁定本地端口進行監聽,然後用戶端進行連接配接服務端,服務端接受連接配接之後雙方建立了一個端對端的TCP連接配接,在該連接配接上就可以雙向通訊了,而且一旦建立這個連接配接之後,通信雙方就沒有用戶端服務端之分了,提供的就是端對端通信了。我們可以采取這種方式建構一個桌面版的im程式,讓不同主機上的使用者發送消息。從本質上來說,socket并不是一個新的協定,它隻是為了便于程式員進行網絡程式設計而對tcp/ip協定族通信機制的一種封裝。
websocket是html5規範中的一個部分,它借鑒了socket這種思想,為web應用程式用戶端和服務端之間(注意是用戶端服務端)提供了一種全雙工通信機制。同時,它又是一種新的應用層協定,websocket協定是為了提供web應用程式和服務端全雙工通信而專門制定的一種應用層協定,通常它表示為:ws://echo.websocket.org/?encoding=text HTTP/1.1,可以看到除了前面的協定名和http不同之外,它的表示位址就是傳統的url位址。
可以看到,websocket并不是簡單地将socket這一概念在浏覽器環境中的移植
2、WebSocket的通信原理和機制
既然是基于浏覽器端的web技術,那麼它的通信肯定少不了http,websocket本身雖然也是一種新的應用層協定,但是它也不能夠脫離http而單獨存在。具體來講,我們在用戶端建構一個websocket執行個體,并且為它綁定一個需要連接配接到的伺服器位址,當用戶端連接配接服務端的時候,會向服務端發送一個類似下面的http封包:
可以看到,這是一個http get請求封包,注意該封包中有一個upgrade首部,它的作用是告訴服務端需要将通信協定切換到websocket,如果服務端支援websocket協定,那麼它就會将自己的通信協定切換到websocket,同時發給用戶端類似于以下的一個響應封包頭:
傳回的狀态碼為101,表示同意用戶端協定轉換請求,并将它轉換為websocket協定。以上過程都是利用http通信完成的,稱之為websocket協定握手(websocket Protocol handshake),進過這握手之後,用戶端和服務端就建立了websocket連接配接,以後的通信走的都是websocket協定了。是以總結為websocket握手需要借助于http協定,建立連接配接後通信過程使用websocket協定。同時需要了解的是,該websocket連接配接還是基于我們剛才發起http連接配接的那個TCP連接配接。一旦建立連接配接之後,我們就可以進行資料傳輸了,websocket提供兩種資料傳輸:文本資料和二進制資料。
基于以上分析,我們可以看到,websocket能夠提供低延遲,高性能的用戶端與服務端的雙向資料通信。它颠覆了之前web開發的請求處理響應模式,并且提供了一種真正意義上的用戶端請求,伺服器推送資料的模式,特别适合實時資料互動應用開發。
3、WebSocket技術出現之前,Web端實作即時通訊的方法有哪些?
1、定期輪詢的方式
用戶端按照某個時間間隔不斷地向服務端發送請求,請求服務端的最新資料然後更新用戶端顯示。這種方式實際上浪費了大量流量并且對服務端造成了很大壓力。
2、SSE(Server-Sent Event,服務端推送事件)
SSE(Server-Sent Event,服務端推送事件)是一種允許服務端向用戶端推送新資料的HTML5技術。與由用戶端每隔幾秒從服務端輪詢拉取新資料相比,這是一種更優的解決方案。
相較于WebSocket,它也能從服務端向用戶端推送資料。WebSocket能做的,SSE也能做,反之亦然,但在完成某些任務方面,它們各有千秋。關于SSE的介紹,即時通訊網将在稍後的文章中詳細介紹。
3、Comet技術
Comet并不是一種新的通信技術,它是在用戶端請求服務端這個模式上的一種hack技術,通常來講,它主要分為以下兩種做法:
(1)基于長輪詢的服務端推送技術
具體來講,就是用戶端首先給服務端發送一個請求,服務端收到該請求之後如果資料沒有更新則并不立即傳回,服務端阻塞請求的傳回,直到資料發生了更新或者發生了連接配接逾時,服務端傳回資料之後用戶端再次發送同樣的請求,如下所示:
2)基于流式資料傳輸的長連接配接
通常的做法是在頁面中嵌入一個隐藏的iframe,然後讓這個iframe的src屬性指向我們請求的一個服務端位址,并且為了資料更新,我們将頁面上資料更新操作封裝為一個js函數,将函數名當做參數傳遞到這個位址當中。
服務端收到請求後解析位址取出參數(用戶端js函數調用名),每當有資料更新的時候,傳回對用戶端函數的調用,并且将要跟新的資料以js函數的參數填入到傳回内容當中,例如傳回“”這樣一個字元串,意味着以data為參數調用用戶端update函數進行用戶端view更新。基本模型如下所示:
可以看到comet技術是針對用戶端請求伺服器響應模型而模拟出的一個服務端推送資料實時更新技術。而且由于浏覽器相容性不能夠廣泛應用。
4、小結
當然并不是說這些技術沒有用,就算websocket已經作為規範被提出并實作,但是對于老式浏覽器,我們依然需要将它降級為以上方式來實作實時互動和服務端資料推送。
4、一個簡單的WebSocket聊天小例子
到此為止,我們明白了websocket的原理,下面通過一個簡單的聊天應用來再次加深下對websocket的了解。該應用需求很簡單,就是在web頁籤中打開兩個網頁,模拟兩個web用戶端實作聊天功能。
1用戶端代碼
client.html:
<!DOCTYPE html>
<html>
<head lang="en">
<meta charset="UTF-8">
<title></title>
<style> *{
margin: 0; padding: 0; } .message{
width: 60%; margin: 0 10px; display: inline-block; text-align: center; height: 40px; line-height: 40px; font-size: 20px; border-radius: 5px; border: 1px solid #B3D33F; } .form{
width:100%; position: fixed; bottom: 300px; left: 0; } .connect{
height: 40px; vertical-align: top; /* padding: 0; */ width: 80px; font-size: 20px; border-radius: 5px; border: none; background: #B3D33F; color: #fff; } </style>
</head>
<body>
<ul id="content"></ul>
<form class="form">
<input type="text" placeholder="請輸入發送的消息" class="message" id="message"/>
<input type="button" value="發送" id="send" class="connect"/>
<input type="button" value="連接配接" id="connect" class="connect"/>
</form>
<script></script>
</body>
</html>
複制
用戶端js代碼:
var oUl=document.getElementById('content');
var oConnect=document.getElementById('connect');
var oSend=document.getElementById('send');
var oInput=document.getElementById('message');
var ws=null;
oConnect.onclick=function(){
ws=new WebSocket('ws://localhost:5000');
ws.onopen=function(){
oUl.innerHTML+="<li>用戶端已連接配接</li>";
}
ws.onmessage=function(evt){
oUl.innerHTML+="<li>"+evt.data+"</li>";
}
ws.onclose=function(){
oUl.innerHTML+="<li>用戶端已斷開連接配接</li>";
};
ws.onerror=function(evt){
oUl.innerHTML+="<li>"+evt.data+"</li>";
};
};
oSend.onclick=function(){
if(ws){
ws.send(oInput.value);
}
}
複制
這裡使用的是w3c規範中關于HTML5 websocket API的原生API,這些api很簡單,就是利用new WebSocket建立一個指定連接配接服務端位址的ws執行個體,然後為該執行個體注冊onopen(連接配接服務端),onmessage(接受服務端資料),onclose(關閉連接配接)以及ws.send(建立連接配接後)發送請求。上面說了那麼多,事實上可以看到html5 websocket API本身是很簡單的一個對象和它的幾個方法而已。
2服務端代碼
服務端采用Node.js,這裡需要基于一個nodejs-websocket的Node.js服務端的庫,它是一個輕量級的Node.js websocket server端的實作,實際上也是使用Node.js提供的net子產品寫成的。
server.js:
var app=require('http').createServer(handler);
var ws=require('nodejs-websocket');
var fs=require('fs');
app.listen(80);
function handler(req,res){
fs.readFile(__dirname+'/client.html',function(err,data){
if(err){
res.writeHead(500);
return res.end('error ');
}
res.writeHead(200);
res.end(data);
});
}
var server=ws.createServer(function(conn){
console.log('new conneciton');
conn.on("text",function(str){
broadcast(server,str);
});
conn.on("close",function(code,reason){
console.log('connection closed');
})
}).listen(5000);
function broadcast(server, msg) {
server.connections.forEach(function (conn) {
conn.sendText(msg);
})
}
複制
首先利用http子產品監聽使用者的http請求并顯示client.html界面,然後建立一個websocket服務端等待使用者連接配接,在接收到使用者發送來的資料之後将它廣播到所有連接配接到的用戶端。
3運作
下面我們打開兩個浏覽器頁籤模拟兩個用戶端進行連接配接。
用戶端一發起連接配接:
用戶端一請求響應封包如下:
可以看到這次握手和我們之前講的如出一轍。
用戶端二的連接配接過程和1是一樣的,這裡為了檢視我們使用ff浏覽器,兩個用戶端的連接配接情況如下:
發送消息情況如下:
可以看到,雙方發送的消息被服務端廣播給了每個和自己連接配接的用戶端。
5、結語
從上面的即時通訊聊天例子我們可以看到,要想做一個點對點的im應用,websocket采取的方式是讓所有用戶端連接配接服務端,伺服器将不同用戶端發送給自己的消息進行轉發或者廣播,而對于原始的socket,隻要兩端建立連接配接之後,就可以發送端對端的資料,不需要經過第三方的轉發(即時通訊網注:原文作者指的是原生的Socket可以通過P2P直接進行消息互動,實際現今主流的IM應用幾乎都是使用服務端中轉的方式進行文本類消息的發送,使用中轉無關技術,主要是基于營運考慮),這也是websocket不同于socket的一個重要特點。
最後,本文為了說明html5規範中的websocket在用戶端采用了websocket原生的API,實際開發中,有比較著名的兩個庫socket.io和sockjs,它們都對原始的websocket API做了進一步封裝,提供了更多功能,都分為用戶端和服務端的實作,實際應用中,可以選擇使用。
知乎上的這篇《WebSocket 是什麼原理?為什麼可以實作持久連接配接?》,比較通俗,适合快餐式地了解WebSocket。
參考文章:http://www.52im.net/forum.php?mod=viewthread&tid=331&ctid=15
釋出者:全棧程式員棧長,轉載請注明出處:https://javaforall.cn/157438.html原文連結:https://javaforall.cn