欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

JavaScript實(shí)現(xiàn)即時(shí)通訊的 4 種方案

 更新時(shí)間:2022年02月14日 09:23:08   作者:隱冬?  
這篇文章主要給大家分享了JavaScript實(shí)現(xiàn)即時(shí)通訊的 4 種方案,其實(shí)就是服務(wù)端如何將數(shù)據(jù)推送到瀏覽器,下面詳細(xì)的文章內(nèi)容,需要的小伙伴參考一下,洗碗給對你有所幫助

前言:

服務(wù)端如何將數(shù)據(jù)推送到瀏覽器,一般來說,Web端即時(shí)通訊技術(shù)因受限于瀏覽器的設(shè)計(jì)限制,一直以來實(shí)現(xiàn)起來并不容易,這里總結(jié)了4種方式。

1. http + ajax

XMLHttpRequest在和服務(wù)端進(jìn)行數(shù)據(jù)交互時(shí)存在四種狀態(tài),很多時(shí)候的判斷是readyState為4時(shí),從response獲取服務(wù)端響應(yīng)結(jié)果。其實(shí)readyState等于3的時(shí)候就可以獲取到服務(wù)端的部分?jǐn)?shù)據(jù)了。

可以利用這個(gè)屬性實(shí)現(xiàn)服務(wù)端推送。

比如服務(wù)使用http創(chuàng)建服務(wù),每間隔1s的時(shí)候通過write方法返回一段文本,但是不要調(diào)用end方法。

const http = require('http');
const fs = require('fs');

const app = http.createServer((req, res) => {
? ? // 設(shè)置響應(yīng)頭
? ? res.setHeader('Content-type', 'application/json; charset=utf-8');
? ? res.setHeader('Cache-Control', 'max-age=0'); // 沒有緩存
? ? let num = 0;
? ? // 地柜返回
? ? const send = () => {
? ? ? ? if (num > 20) {
? ? ? ? ? ? res.end();
? ? ? ? ? ? return;
? ? ? ? }
? ? ? ? num++;
? ? ? ? const data = Math.random() + '';
? ? ? ? res.write(data, 'utf8');
? ? ? ? setTimeout(send, 1000);
? ? }
? ? send();
});

app.listen(8081, () => {
? ? console.log('127.0.0.1:8081');
})

前端監(jiān)聽XMLHttpRequestonreadystatechange事件,每當(dāng)服務(wù)器返回一段數(shù)據(jù)都會(huì)觸發(fā)一次onreadystatechange事件,可以從responseText中得到當(dāng)前獲取到的全部數(shù)據(jù)。

var xhr = new XMLHttpRequest();
xhr.open('GET', '/api');
xhr.timeout = 30000;
xhr.responseType = 'text';
xhr.onreadystatechange = function () {
? ? if (this.readyState == 3) { // 分段獲取服務(wù)端返回的數(shù)據(jù)
? ? ? ? console.log(this.responseText);
? ? }
? ? if (this.readyState == 4) {
? ? ? ? if (this.status >= 200 && this.status < 300 || this.status == 304) {
? ? ? ? ? ? // this.response
? ? ? ? } else {
? ? ? ? ? ? // this.statusText
? ? ? ? }
? ? }
}
xhr.send()

2. websocket

websocket具有三個(gè)優(yōu)點(diǎn),雙向通信,自動(dòng)跨域,性能高。最主要的是可以傳輸多種格式的數(shù)據(jù)。WebSocket 協(xié)議在2008年誕生,2011年成為國際標(biāo)準(zhǔn)。所有瀏覽器都已經(jīng)支持了,也是應(yīng)用很廣泛的一種即時(shí)通信協(xié)議。

websocket是HTML5新增的API,屬于瀏覽器或者前端的內(nèi)容。后端用的是socket,socket協(xié)議的歷史相當(dāng)古老基本四十年前就已經(jīng)存在了。在H5中websocket自帶一些安全的措施,而原生的socket就沒什么安全性可言了。

客戶端瀏覽器通過實(shí)例化Websocket,傳入服務(wù)地址創(chuàng)建websocket鏈接,message中會(huì)接收到服務(wù)端推送的數(shù)據(jù),也可通過send方法向服務(wù)端發(fā)送數(shù)據(jù)。

const ws = new Websocket('ws://127.0.0.1:8080/api');
// 原生沒有emit,自己封裝一個(gè)
ws.emit = function(name, ...args) {
? ? ws.send(JSON.stringify({
? ? ? ? name,
? ? ? ? data: [...args]
? ? }))
}
ws.onopen = function() {
? ? console.log('鏈接上了');
? ? // ws.send('dadadadadasda'); // 發(fā)送數(shù)據(jù),只有一個(gè)參數(shù)一個(gè)大字符串
? ? ws.emit('msg', 12, 5, 8);
}; // 已經(jīng)鏈接
ws.onmessage = function() {
? ? console.log('接收到消息了')
}; // 收到數(shù)據(jù)
ws.onclose = function() {
? ? console.log('斷開鏈接了')
}; // 斷開了

node中想要實(shí)現(xiàn)socket可以借助node原生的net模塊,這是一個(gè)相對底層的網(wǎng)絡(luò)模塊,是一個(gè)tcp的庫。net是http的底層,很多東西都需要自己去實(shí)現(xiàn),比如這里可以使用net.createServer來創(chuàng)建服務(wù)。

websocket也是給予http的,先通過http請求到服務(wù),會(huì)攜帶一個(gè)upgradewebsocket的請求頭,表示希望升級為websocket,這個(gè)時(shí)候服務(wù)可以返回101狀態(tài)碼,表示進(jìn)行服務(wù)可以升級。

const http = require('http');
const net = require('net'); // TCP的庫,可以理解為原生的Socket
const crypto = require('crypto'); // 借助加密庫實(shí)現(xiàn)一些安全性

const server = net.createServer(sock=> {
? ? console.log('鏈接上了');
? ? sock.on('end', () => {
? ? ? ? console.log('客戶端斷開了')
? ? }); // 斷開

? ? sock.once('data', (data) => {
? ? ? ? console.log('hand shake start...');
? ? ? ? // 最先過來的是http頭
? ? ? ? const str = data.toString();
? ? ? ? // 將http頭用\r\n切開
? ? ? ? let lines = str.split('\r\n');
? ? ? ? // 刪除第一行和最后一行,因?yàn)闆]啥用
? ? ? ? lines = lines.slice(1, lines.length - 2);
? ? ? ? // 將所有請求頭通過'分號(hào)空格'切開
? ? ? ? const headers = {};
? ? ? ? lines.forEach(line => {
? ? ? ? ? ? const [key, value ] = line.split(': ');
? ? ? ? ? ? // 將請求頭變成小寫
? ? ? ? ? ? headers[key.toLowerCase()] = val;
? ? ? ? })
? ? ? ? // http協(xié)議轉(zhuǎn)websocket會(huì)傳入upgrade為websocket
? ? ? ? if (headers['upgrade'] != 'websocket') {
? ? ? ? ? ? console.log('其他協(xié)議,暫不支持');
? ? ? ? ? ? sock.end();
? ? ? ? } else if (headers['sec-websocket-version'] != 13) {
? ? ? ? ? ? console.log('不兼容不是13的版本');
? ? ? ? ? ? sock.end();
? ? ? ? } else {
? ? ? ? ? ? const key = headers['sec-websocket-key'];
? ? ? ? ? ? // 13版本的源碼是258E,可以百度的到
? ? ? ? ? ? const mask = '258EAFA5-47DA-95CA-C5AB0DC85B11';
? ? ? ? ? ? // 需要把key和mask加在一起,然后用sha1加密,再變成base64,還給客戶端
? ? ? ? ? ? // sha1(key + mask) -> base64 -> client;
? ? ? ? ? ? const hash = crypto.createHash('sha1');
? ? ? ? ? ? hash.update(key + mask);
? ? ? ? ? ? const tokey = hash.digest('base64');
? ? ? ? ? ? // 數(shù)據(jù)以HTTP發(fā)回客戶端,因?yàn)轵?yàn)證的過程還是http階段, 狀態(tài)值為101(正在切換協(xié)議,協(xié)議升級 Switching Protocols)
? ? ? ? ? ? sock.write('HTTP/1.1 101 Switching Protocols\r\nUpgrade: websocket\r\nConnection: Upgrade\r\nSec-WebSocket-Accept: ' + tokey + '\r\n'); // Upgrade: websocket告訴瀏覽器升級為websocket,冒號(hào)要有空格
? ? ? ? ? ? // 至此,握手已經(jīng)結(jié)束了。因?yàn)槲帐值倪^程只有一次,所以不要用on處理,用once處理
? ? ? ? ? ? // 從這里開始,才是真正的數(shù)據(jù),以后所有的數(shù)據(jù)都走這里,所以用on處理
? ? ? ? ? ? sock.on('data', data => {
? ? ? ? ? ? ? ? // 獲取到的數(shù)據(jù)
? ? ? ? ? ? ? ? // 不過數(shù)據(jù)是一個(gè)buffer的數(shù)據(jù)包,解析起來比較麻煩。
? ? ? ? ? ? ? ? console.log(data);
? ? ? ? ? ? })
? ? ? ? }
? ? }); // 有數(shù)據(jù)過來
}).listen(8080);

上面介紹的是websocket的一個(gè)實(shí)現(xiàn)原理,項(xiàng)目中可以直接使用socket.io這個(gè)庫。

前端代碼如下:

const sock = io.connect('ws://127.0.0.1:8080/api');
sock.on('connect', () => {
? ? console.log('已鏈接');
? ? sock.emit('aaa', 12, 5,8);
? ? sock.on('time', (ts) => {
? ? ? ? console.loh(ts);
? ? })
});
sock.on('disconnect', () => {
? ? console.log('已斷開');
});

服務(wù)端代碼如下:

const http = require('http');
const io = require('socket.io');

// 創(chuàng)建http服務(wù),開啟8080端口號(hào)
const httpServer = http.createServer().listen(8080);
// socket監(jiān)聽http服務(wù)
const wsServer = io.listen(httpServer);

// 當(dāng)有鏈接的時(shí)候
wsServer.on('connection', sock => {
? ? // 發(fā)送
? ? // sock.emit
? ? sock.emit('time', Date.now());
? ? // 接收
? ? sock.on('aaa', (a, b, c) => {
? ? ? ? console.loh(a, b, c);
? ? })
})

3. SSE

SSE全稱是Server-Sent Events,指的是網(wǎng)頁自動(dòng)獲取來自服務(wù)器的更新,也就是自動(dòng)獲取服務(wù)端推送至網(wǎng)頁的數(shù)據(jù),這是一個(gè)H5的屬性,除了IE,其它標(biāo)準(zhǔn)瀏覽器基本都兼容。

實(shí)現(xiàn)方式和第二種有一些像,服務(wù)器向客戶端聲明要發(fā)送流信息,然后連續(xù)不斷地發(fā)送過來。這時(shí)客戶端是不會(huì)關(guān)閉連接的,會(huì)一直等著服務(wù)器發(fā)過來的新的數(shù)據(jù)流。比如音視頻的媒體流就是這種機(jī)制。

SSE 只能服務(wù)器向?yàn)g覽器發(fā)送數(shù)據(jù),這點(diǎn)和第二種方式很像,能力上都不如websocket,優(yōu)點(diǎn)是SSE使用更加簡單,并且基于http協(xié)議,兼容性還可以(當(dāng)然2022年了,沒有啥是兼容性不可以的了)。

H5端使用EventSource對象,填入要請求的url地址就可以了。

var source = new EventSource('/api', {
? ? withCredentials: true
});
source.onopen = function () {
? ? console.log('鏈接已建立', this.readyState);
}
source.onmessage = function (event) {
? ? console.log('實(shí)時(shí)獲取的數(shù)據(jù)', event.data);
}
source.onerror = function () {
? ? console.log('發(fā)生錯(cuò)誤');
}
// 關(guān)閉
// source.close();

服務(wù)器向?yàn)g覽器發(fā)送的 SSE 數(shù)據(jù),首先必須設(shè)置響應(yīng)頭的Content-typetext/event-stream,且編碼格式為utf-8。返回的數(shù)據(jù)格式必須為data: xxxx\n\n。除了data還有event,id,以及retry,可以參考Server-sent_events-mdn

服務(wù)端代碼如下:

const http = require('http');
const fs = require('fs');

const app = http.createServer((req, res) => {
? ? res.setHeader('Content-type', 'text/event-stream; charset=utf-8');
? ? res.setHeader('Cache-Control', 'max-age=0'); // 清楚緩存
? ? res.setHeader('Access-Control-Allow-Origin', 'http:127.0.0.1/');
? ? let num = 0;
? ? const send = () => {
? ? ? ? if (num > 20) {
? ? ? ? ? ? res.end();
? ? ? ? ? ? return;
? ? ? ? }
? ? ? ? num++;
? ? ? ? const data = Math.random() + '';
? ? ? ? res.write(`data: ${data}\n\n`, 'utf8');
? ? ? ? setTimeout(send, 1000);
? ? }
? ? send();
});

app.listen(8081, () => {
? ? console.log('127.0.0.1:8081');
})

到此這篇關(guān)于JavaScript實(shí)現(xiàn)即時(shí)通訊的 4 種方案的文章就介紹到這了,更多相關(guān)JavaScript實(shí)現(xiàn)即時(shí)通訊方案內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

最新評論