摘要:實時通訊越來越多應用于各個領域。實現原生實現對象一共支持四個消息和。是基于的實時通信庫。服務器應該用包含相同數據的乓包應答客戶端發送探測幀由服務器發送以響應數據包。主要用于在接收到傳入連接時強制輪詢周期。該間隔可通過配置修改。
隨著web技術的發展,使用場景和需求也越來越復雜,客戶端不再滿足于簡單的請求得到狀態的需求。實時通訊越來越多應用于各個領域。
HTTP是最常用的客戶端與服務端的通信技術,但是HTTP通信只能由客戶端發起,無法及時獲取服務端的數據改變。只能依靠定期輪詢來獲取最新的狀態。時效性無法保證,同時更多的請求也會增加服務器的負擔。
WebSocket技術應運而生。
WebSocket概念不同于HTTP半雙工協議,WebSocket是基于TCP 連接的全雙工協議,支持客戶端服務端雙向通信。
WebSocket使得客戶端和服務器之間的數據交換變得更加簡單,允許服務端主動向客戶端推送數據。在 WebSocket API 中,瀏覽器和服務器只需要完成一次握手,兩者之間就直接可以創建持久性的連接,并進行雙向數據傳輸。
在WebSocket API中,瀏覽器和服務器只需要做一個握手的動作,然后,瀏覽器和服務器之間就形成了一條快速通道。兩者之間就直接可以數據互相傳送。
實現 原生實現WebSocket對象一共支持四個消息 onopen, onmessage, onclose和onerror。
建立連接通過javascript可以快速的建立一個WebSocket連接:
var Socket = new WebSocket(url, [protocol] );
以上代碼中的第一個參數url, 指定連接的URL。第二個參數 protocol是可選的,指定了可接受的子協議。
同http協議使用http://開頭一樣,WebSocket協議的URL使用ws://開頭,另外安全的WebSocket協議使用wss://開頭。
當Browser和WebSocketServer連接成功后,會觸發onopen消息。
Socket.onopen = function(evt) {};
如果連接失敗,發送、接收數據失敗或者處理數據出現錯誤,browser會觸發onerror消息。
Socket.onerror = function(evt) { };
當Browser接收到WebSocketServer端發送的關閉連接請求時,就會觸發onclose消息。
Socket.onclose = function(evt) { };收發消息
當Browser接收到WebSocketServer發送過來的數據時,就會觸發onmessage消息,參數evt中包含server傳輸過來的數據。
Socket.onmessage = function(evt) { };
send用于向服務端發送消息。
Socket.send();socket
WebSocket是跟隨HTML5一同提出的,所以在兼容性上存在問題,這時一個非常好用的庫就登場了——Socket.io。
socket.io封裝了websocket,同時包含了其它的連接方式,你在任何瀏覽器里都可以使用socket.io來建立異步的連接。socket.io包含了服務端和客戶端的庫,如果在瀏覽器中使用了socket.io的js,服務端也必須同樣適用。
socket.io是基于 Websocket 的Client-Server 實時通信庫。
socket.io底層是基于engine.io這個庫。engine.io為 socket.io 提供跨瀏覽器/跨設備的雙向通信的底層庫。engine.io使用了 Websocket 和 XHR 方式封裝了一套 socket 協議。在低版本的瀏覽器中,不支持Websocket,為了兼容使用長輪詢(polling)替代。
API文檔
Socket.io允許你觸發或響應自定義的事件,除了connect,message,disconnect這些事件的名字不能使用之外,你可以觸發任何自定義的事件名稱。
建立連接const socket = io("ws://0.0.0.0:port"); // port為自己定義的端口號 let io = require("socket.io")(http); io.on("connection", function(socket) {})消息收發
一、發送數據
socket.emit(自定義發送的字段, data);
二、接收數據
socket.on(自定義發送的字段, function(data) { console.log(data); })斷開連接
一、全部斷開連接
let io = require("socket.io")(http); io.close();
二、某個客戶端斷開與服務端的鏈接
// 客戶端 socket.emit("close", {});
// 服務端 socket.on("close", data => { socket.disconnect(true); });room和namespace
有時候websocket有如下的使用場景:1.服務端發送的消息有分類,不同的客戶端需要接收的分類不同;2.服務端并不需要對所有的客戶端都發送消息,只需要針對某個特定群體發送消息;
針對這種使用場景,socket中非常實用的namespace和room就上場了。
先來一張圖看看namespace與room之間的關系:
namespace服務端
io.of("/post").on("connection", function(socket) { socket.emit("new message", { mess: `這是post的命名空間` }); }); io.of("/get").on("connection", function(socket) { socket.emit("new message", { mess: `這是get的命名空間` }); });
客戶端
// index.js const socket = io("ws://0.0.0.0:****/post"); socket.on("new message", function(data) { console.log("index",data); } //message.js const socket = io("ws://0.0.0.0:****/get"); socket.on("new message", function(data) { console.log("message",data); }room
客戶端
//可用于客戶端進入房間; socket.join("room one"); //用于離開房間; socket.leave("room one");
服務端
io.sockets.on("connection",function(socket){ //提交者會被排除在外(即不會收到消息) socket.broadcast.to("room one").emit("new messages", data); // 向所有用戶發送消息 io.sockets.to(data).emit("recive message", "hello,房間中的用戶"); }用socket.io實現一個實時接收信息的例子
終于來到應用的階段啦,服務端用node.js模擬了服務端接口。以下的例子都在本地服務器中實現。
服務端先來看看服務端,先來開啟一個服務,安裝express和socket.io
安裝依賴npm install --Dev express npm install --Dev socket.io構建node服務器
let app = require("express")(); let http = require("http").createServer(handler); let io = require("socket.io")(http); let fs = require("fs"); http.listen(port); //port:輸入需要的端口號 function handler(req, res) { fs.readFile(__dirname + "/index.html", function(err, data) { if (err) { res.writeHead(500); return res.end("Error loading index.html"); } res.writeHead(200); res.end(data); }); } io.on("connection", function(socket) { console.log("連接成功"); //連接成功之后發送消息 socket.emit("new message", { mess: `初始消息` }); });客戶端
核心代碼——index.html(向服務端發送數據)
發送信息
// 接收到服務端傳來的name匹配的消息 socket.on("new message", function(data) { console.log(data); }); function postMessage() { socket.emit("recive message", { message: content, time: new Date() }); messList.push({ message: content, time: new Date() }); }
核心代碼——message.html(從服務端接收數據)
socket.on("new message", function(data) { console.log(data); });效果
實時通訊效果
客戶端全部斷開連接
某客戶端斷開連接
namespace應用
加入房間
離開房間
npm install socket.io-client
const socket = require("socket.io-client")("http://localhost:port"); componentDidMount() { socket.on("login", (data) => { console.log(data) }); socket.on("add user", (data) => { console.log(data) }); socket.on("new message", (data) => { console.log(data) }); }分析webSocket協議 Headers 請求包
Accept-Encoding: gzip, deflate Accept-Language: zh-CN,zh;q=0.9,en;q=0.8 Cache-Control: no-cache Connection: Upgrade Cookie: MEIQIA_VISIT_ID=1IcBRlE1mZhdVi1dEFNtGNAfjyG; token=0b81ffd758ea4a33e7724d9c67efbb26; io=ouI5Vqe7_WnIHlKnAAAG Host: 0.0.0.0:2699 Origin: http://127.0.0.1:5500 Pragma: no-cache Sec-WebSocket-Extensions: permessage-deflate; client_max_window_bits Sec-WebSocket-Key: PJS0iPLxrL0ueNPoAFUSiA== Sec-WebSocket-Version: 13 Upgrade: websocket User-Agent: Mozilla/5.0 (iPhone; CPU iPhone OS 11_0 like Mac OS X) AppleWebKit/604.1.38 (KHTML, like Gecko) Version/11.0 Mobile/15A372 Safari/604.1
請求包說明:
必須是有效的http request 格式;
HTTP request method 必須是GET,協議應不小于1.1 如: Get / HTTP/1.1;
必須包括Upgrade頭域,并且其值為“websocket”,用于告訴服務器此連接需要升級到websocket;
必須包括”Connection” 頭域,并且其值為“Upgrade”;
必須包括”Sec-WebSocket-Key”頭域,其值采用base64編碼的隨機16字節長的字符序列;
如果請求來自瀏覽器客戶端,還必須包括Origin頭域 。 該頭域用于防止未授權的跨域腳本攻擊,服務器可以從Origin決定是否接受該WebSocket連接;
必須包括“Sec-webSocket-Version”頭域,是當前使用協議的版本號,當前值必須是13;
可能包括“Sec-WebSocket-Protocol”,表示client(應用程序)支持的協議列表,server選擇一個或者沒有可接受的協議響應之;
可能包括“Sec-WebSocket-Extensions”, 協議擴展, 某類協議可能支持多個擴展,通過它可以實現協議增強;
可能包括任意其他域,如cookie.
應答包應答包說明:
Connection: Upgrade Sec-WebSocket-Accept: I4jyFwm0r1J8lrnD3yN+EvxTABQ= Sec-WebSocket-Extensions: permessage-deflate Upgrade: websocket
必須包括Upgrade頭域,并且其值為“websocket”;
必須包括Connection頭域,并且其值為“Upgrade”;
必須包括Sec-WebSocket-Accept頭域,其值是將請求包“Sec-WebSocket-Key”的值,與”258EAFA5-E914-47DA-95CA-C5AB0DC85B11″這個字符串進行拼接,然后對拼接后的字符串進行sha-1運算,再進行base64編碼,就是“Sec-WebSocket-Accept”的值;
應答包中冒號后面有一個空格;
最后需要兩個空行作為應答包結束。
請求數據EIO: 3 transport: websocket sid: 8Uehk2UumXoHVJRzAAAA
EIO:3 表示使用的是engine.io協議版本3
transport 表示傳輸采用的類型
sid: session id (String)
FramesWebSocket協議使用幀(Frame)收發數據,在控制臺->Frames中可以查看發送的幀數據。
其中幀數據前的數字代表什么意思呢?
這是 Engine.io協議,其中的數字是數據包編碼:
0 open——在打開新傳輸時從服務器發送(重新檢查)
1 close——請求關閉此傳輸,但不關閉連接本身。
2 ping——由客戶端發送。服務器應該用包含相同數據的乓包應答
客戶端發送:2probe探測幀
3 pong——由服務器發送以響應ping數據包。
服務器發送:3probe,響應客戶端
4 message——實際消息,客戶端和服務器應該使用數據調用它們的回調。
5 upgrade——在engine.io切換傳輸之前,它測試,如果服務器和客戶端可以通過這個傳輸進行通信。如果此測試成功,客戶端發送升級數據包,請求服務器刷新其在舊傳輸上的緩存并切換到新傳輸。
6 noop——noop數據包。主要用于在接收到傳入WebSocket連接時強制輪詢周期。
實例
以上的截圖是上述例子中數據傳輸的實例,分析一下大概過程就是:
connect握手成功
客戶端會發送2 probe探測幀
服務端發送響應幀3probe
客戶端會發送內容為5的Upgrade幀
服務端回應內容為6的noop幀
探測幀檢查通過后,客戶端停止輪詢請求,將傳輸通道轉到websocket連接,轉到websocket后,接下來就開始定期(默認是25秒)的 ping/pong
客戶端、服務端收發數據,4表示的是engine.io的message消息,后面跟隨收發的消息內容
為了知道Client和Server鏈接是否正常,項目中使用的ClientSocket和ServerSocket都有一個心跳的線程,這個線程主要是為了檢測Client和Server是否正常鏈接,Client和Server是否正常鏈接主要是用ping pong流程來保證的。
該心跳定期發送的間隔是socket.io默認設定的25m,在上圖中也可觀察發現。該間隔可通過配置修改。
參考engine.io-protocol
參考文章Web 實時推送技術的總結
engine.io 原理詳解
本文發布于薄荷前端周刊,歡迎Watch & Star ★,轉載請注明出處。
歡迎討論,點個贊再走吧 ????? ~文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/103757.html
摘要:聊一聊端的即時通訊端實現即時通訊的方法有哪些短輪詢長輪詢流輪詢客戶端定時向服務器發送請求,服務器接到請求后馬上返回響應信息并關閉連接。介紹是開始提供的一種在單個連接上進行全雙工通訊的協議。 聊一聊Web端的即時通訊 Web端實現即時通訊的方法有哪些? - 短輪詢 長輪詢 iframe流 Flash Socket 輪詢 客戶端定時向服務器發送Ajax請求,服務器接到請求后馬上返...
摘要:在標準沒有推出之前,輪詢是唯一可行的方式通過瀏覽器也可以,但這里不做討論。這種方式存在延時且對服務端造成很大負載。直到年,才標準化一種基于套接字進行收發數據的協議。現如今主流瀏覽器均已支持。 socket.io提供了基于事件的實時雙向通訊 歷史 Web端與服務器間的實時數據傳輸的是一個很重要的需求,但最早只能通過AJAX輪詢詢實現。在WebSocket標準沒有推出之前,AJAX輪詢是唯...
閱讀 2092·2021-11-02 14:48
閱讀 2768·2019-08-30 14:19
閱讀 2937·2019-08-30 13:19
閱讀 1305·2019-08-29 16:17
閱讀 3243·2019-08-26 14:05
閱讀 2997·2019-08-26 13:58
閱讀 3084·2019-08-23 18:10
閱讀 1112·2019-08-23 18:04