導航:首頁 > 編程語言 > nodejswebsocket性能

nodejswebsocket性能

發布時間:2023-03-23 21:34:44

A. 如何對基於node.js 的websocket進行並發訪問的性能測試

java、c#、php都有專門的測試工具去測試並發、負載均衡、平肆芹皮首兆滑性、代碼裂差覆蓋率等的,node這方面還在研究中,空間很大
自己寫個模擬客戶端測試唄,nodejs也有websocket client的實現
不過性能的話不要報太大希望

B. websocket和nodejs有什麼聯系

Node.js 是一個 JavaScript 運行環境。
WebSocket協議是基於TCP的一種新的網路協議。它實現了瀏覽器與伺服器全雙工(full-plex)通信——允許伺服器主動發送信息給客戶端。
Node.js可以作為WebSocket的服務端,但WebSocket也可以使用其它的服務端。

C. nodejs-websocket介紹

websocket 是一種網路通信協議,一般用來進行實時通信會使用到

websocket 協議和 http 協議類似,http 協議有一個缺陷,只能由客戶方端發起請求,服務端根據請求 url 和傳過去的參數返回對應結果

websocket 是雙向通信的,只要 websocket 連接建立起來,可以由客戶端給服務端發送數據,也可以由服務端主動給客戶端發送數據

websocket 適用場景:聊天室

websocket 相關簡介,可以看 阮老師的文章

nodejs 可以通過 nodejs-websocket 來實現創建一個 websocket 的服務

nodejs-websocket 用法

文檔地址: https://www.npmjs.com/package/nodejs-websocket

node 創建的 websocket 服務,主要包含三個概念

可以通過 server.on('event', (res) => {console.log(res)}) 調用

這次使用 websocket 實現一個基本的聊天室功能,個人感覺還比較簡單,只是中間會出現一些由於鏈接異常斷開,導致後端服務拋出異常掛掉的情況
記住前端關閉頁面或者刷新頁面時,先把連接關掉,每次進入頁面時創建連接,然後後端將由於異常關閉導致的出錯 try/catch 一下,避免拋出異常,阻塞進程

websocket 對於實現聊天室這樣的功能,真的很方便,其實還能擴展到多人合作或者網路游戲等功能

D. nodejs+webSocket

案喊裂例目錄:

1.點對點.js

2.瀑鄭碼閉布模模知式.js

index.html

E. nodejs 承載 多少 websocket

首先是協議的升級,這個比較簡單,就簡述一下:當在客戶端執行new
Websocket("ws://XXX.com/")的時候,客戶端就會發起請求報文進行握手申請,報文中有個很重要的key就是Sec-
WebSocket-Key,服務端獲取到key,然後將這個key與字元串258EAFA5-E914-47DA-95CA-C5AB0DC85B11
相連,對新的字元串通過sha1安全散列演算法計算出結果後,再進行base64編碼,並且將結果放在請求頭的"Sec-WebSocket-
Accept"中返回即可完成握手。具體請看代碼:
server.on('upgrade', function (req, socket, upgradeHead) {
var key = req.headers['sec-websocket-key'];
key = crypto.createHash("sha1").update(key + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11").digest("base64");
var headers = [
'HTTP/1.1 101 Switching Protocols',
'Upgrade: websocket',
'Connection: Upgrade',
'Sec-WebSocket-Accept: ' + key
];
socket.setNoDelay(true);
socket.write(headers.join("\r\n") + "\r\n\r\n", 'ascii');
var ws = new WebSocket(socket);
webSocketCollector.push(ws);
callback(ws);
});

upgrade事件其實是http這個模塊的封裝,再往底層就是net模塊的實現,其實都差不多,如果直接用net模塊來實現的話,就是監聽net.createServer返回的server對象的data事件,接收到的第一份數據就是客戶端發來的升級請求報文。

上面那段代碼就完成了websocket的握手,然後就可以開始數據傳輸了。

看數據傳輸之前,先看看websocket數據幀的定義(因為覺得深入淺出nodejs里的幀定義圖最容易理解,所以就貼這張了):

上面的圖中,每一列就是一個位元組,一個位元組總共是8位,每一位就是一個二進制數,不同位的值會對應不同的意義。

fin:指示這個是消息的最後片段。第一個片段可能也是最後的片段。如果為1即為最後片段,(其實這個位的用途我個人有點疑惑,按照書上以及網上查的資
料,當數據被分片的時候,不同片應該都會有fin位,會根據fin為是不是0來判斷是否為最後一幀,但是實際實現中卻發現,當數據比較大需要分片時,服務
端收到的數據就只有第一幀是有fin位為1,其他幀則整個幀都是數據段,也就是說,感覺這個fin位似乎用不上,至少我自己寫的demo中是通過數據長度
來判斷是否到了最後一幀,完全沒用到這個fin位是否為1來判斷)

rsv1、rsv2、rsv3:各佔一個位,用於擴展協商,基本上不怎麼需要理,一般都是0

opcode:佔四個位,可以表示0~15的十進制,0表示為附加數據幀,1表示為文本數據幀,2表示二進制數據幀,8表示發送一個連接關閉的數據幀,9
表示ping,10表示pong,ping和pong都是用於心跳檢測,當一端發送ping時,另一端必須響應pong表示自己仍處於響應狀態。

masked:佔一個位,表示是否進行掩碼處理,客戶端發送給服務端時為1,服務端發送給客戶端時為0

payload
length:佔7位,或者7+16位、或者7+64位。如果第二個位元組的後面七個位的十進制值小於或等於125,則直接用這七個位表示數據長度;如果該
值為126,說明
125<數據長度<65535(16個位能描述的最大值,也就是16個1的時候),就用第三個位元組及第四個位元組即16個位來表示;如果該值為
127,則說明數據長度已經大於65535,16個位也已經不足以描述數據長度了,就用第三到第十個位元組這八個位元組來描述數據長度。

masking key:當masked為1的時候才存在,用於對我們需要的數據進行解密。

payload data:我們需要的數據,如果masked為1,該數據會被加密,要通過masking key進行異或運算解密才能獲取到真實數據。

幀定義解釋完了,就可以根據數據來進行解析了,當有data過來的時候,先獲取需要的數據信息,下面這段代碼將獲取到數據在data里的位置,以及數據長度,masking key以及opcode:
WebSocket.prototype.handleDataStat = function (data) {
if (!this.stat) {
var dataIndex = 2; //數據索引,因為第一個位元組和第二個位元組肯定不為數據,所以初始值為2
var secondByte = data[1]; //代表masked位和可能是payloadLength位的第二個位元組
var hasMask = secondByte >= 128; //如果大於或等於128,說明masked位為1
secondByte -= hasMask ? 128 : 0; //如果有掩碼,需要將掩碼那一位去掉
var dataLength, maskedData;
//如果為126,則後面16位長的數據為數據長度,如果為127,則後面64位長的數據為數據長度
if (secondByte == 126) {
dataIndex += 2;
dataLength = data.readUInt16BE(2);
} else if (secondByte == 127) {
dataIndex += 8;
dataLength = data.readUInt32BE(2) + data.readUInt32BE(6);
} else {
dataLength = secondByte;
}
//如果有掩碼,則獲取32位的二進制masking key,同時更新index
if (hasMask) {
maskedData = data.slice(dataIndex, dataIndex + 4);
dataIndex += 4;
}
//數據量最大為10kb
if (dataLength > 10240) {
this.send("Warning : data limit 10kb");
} else {
//計算到此處時,dataIndex為數據位的起始位置,dataLength為數據長度,maskedData為二進制的解密數據
this.stat = {
index: dataIndex,
totalLength: dataLength,
length: dataLength,
maskedData: maskedData,
opcode: parseInt(data[0].toString(16).split("")[1] , 16) //獲取第一個位元組的opcode位
};
}
} else {
this.stat.index = 0;
}
};

代碼中均有注釋,理解起來應該不難,直接看下一步,獲取到數據信息後,就要對數據進行實際解析了:

經過上面handleDataStat方法的處理,stat中已經有了data的相關數據,先判斷opcode,如果為9說明是客戶端發起的
ping心跳檢測,直接返回pong響應,如果為10則為服務端發起的心跳檢測。如果有masking
key,則遍歷數據段,對每個位元組都與masking
key的位元組進行異或運算(網上看到一個說法很形象:就是輪流發生X關系),^符號就是進行異或運算啦。如果沒有masking
key則直接通過slice方法把數據截取下來。

獲取到數據後,放進datas里保存,因為有可能數據被分片了,所以再將stat里的長度減去當前數據長度,只有當stat里的長度為0的時候,
說明當前幀為最後一幀,然後通過Buffer.concat將所有數據合並,此時再判斷一下opcode,如果opcode為8,則說明客戶端發起了一個
關閉請求,而我們獲取到的數據則是關閉原因。如果不為8,則這數據就是我們需要的數據。然後再將stat重置為null,datas數組置空即可。至此,
我們的數據解析就完成了。
WebSocket.prototype.dataHandle = function (data) {
this.handleDataStat(data);
var stat;
if (!(stat = this.stat)) return;
//如果opcode為9,則發送pong響應,如果opcode為10則置pingtimes為0
if (stat.opcode === 9 || stat.opcode === 10) {
(stat.opcode === 9) ? (this.sendPong()) : (this.pingTimes = 0);
this.reset();
return;
}
var result;
if (stat.maskedData) {
result = new Buffer(data.length-stat.index);
for (var i = stat.index, j = 0; i < data.length; i++, j++) {
//對每個位元組進行異或運算,masked是4個位元組,所以%4,藉此循環
result[j] = data[i] ^ stat.maskedData[j % 4];
}
} else {
result = data.slice(stat.index, data.length);
}
this.datas.push(result);
stat.length -= (data.length - stat.index);
//當長度為0,說明當前幀為最後幀
if (stat.length == 0) {
var buf = Buffer.concat(this.datas, stat.totalLength);
if (stat.opcode == 8) {
this.close(buf.toString());
} else {
this.emit("message", buf.toString());
}
this.reset();
}
};

完成了客戶端發來的數據解析,還需要一個服務端發數據至客戶端的方法,也就是按照上面所說的幀定義來組裝數據並且發送出去。下面的代碼中基本上每一行都有注釋,應該還是比較容易理解的。
//數據發送
WebSocket.prototype.send = function (message) {
if(this.state !== "OPEN") return;
message = String(message);
var length = Buffer.byteLength(message);
// 數據的起始位置,如果數據長度16位也無法描述,則用64位,即8位元組,如果16位能描述則用2位元組,否則用第二個位元組描述
var index = 2 + (length > 65535 ? 8 : (length > 125 ? 2 : 0));
// 定義buffer,長度為描述位元組長度 + message長度
var buffer = new Buffer(index + length);
// 第一個位元組,fin位為1,opcode為1
buffer[0] = 129;
// 因為是由服務端發至客戶端,所以無需masked掩碼
if (length > 65535) {
buffer[1] = 127;
// 長度超過65535的則由8個位元組表示,因為4個位元組能表達的長度為4294967295,已經完全夠用,因此直接將前面4個位元組置0
buffer.writeUInt32BE(0, 2);
buffer.writeUInt32BE(length, 6);
} else if (length > 125) {
buffer[1] = 126;
// 長度超過125的話就由2個位元組表示
buffer.writeUInt16BE(length, 2);
} else {
buffer[1] = length;
}
// 寫入正文
buffer.write(message, index);
this.socket.write(buffer);
};

最後還要實現一個功能,就是心跳檢測:防止服務端長時間不與客戶端交互而導致客戶端關閉連接,所以每隔十秒都會發送一次ping進行心跳檢測
//每隔10秒進行一次心跳檢測,若連續發出三次心跳卻沒收到響應則關閉socket
WebSocket.prototype.checkHeartBeat = function () {
var that = this;
setTimeout(function () {
if (that.state !== "OPEN") return;
if (that.pingTimes >= 3) {
that.close("time out");
return;
}
//記錄心跳次數
that.pingTimes++;
that.sendPing();
that.checkHeartBeat();
}, 10000);
};
WebSocket.prototype.sendPing = function () {
this.socket.write(new Buffer(['0x89', '0x0']))
};
WebSocket.prototype.sendPong = function () {
this.socket.write(new Buffer(['0x8A', '0x0']))
};

至此,整個websocket的實現就完成了,此demo只是大概實現了一下websocket而已,在安全之類方面肯定還是有很多問題,若是真正生產環境中還是用socket.io這類成熟的插件比較好。不過這還是很值得一學的。

F. WebSocket 的實現

長連接: 一個鏈接上可以連續發送多個數據包,在鏈接期間,如果沒有數據包發送,需要雙方發鏈路檢查包

TCP/IP: TCP/IP 屬於傳輸層,主要解決網路中的數據傳輸問題,只管傳輸數據。但這樣對傳輸的數據沒有一個規范的封裝、解析等處理。使得傳輸的數據難前咐以識別,所以才有了應用層協議對數據進行的封裝、解析等,如http協議。

HTTP: HTTP協議是慧虛純應用層協議,用於分裝解析傳輸數據。 從HTTP1.1開始其實就默認開啟了長鏈接,也就是請求頭header中可以看到Connection:Keep-alive。但是長連接只是說保持了(伺服器可以告訴客戶端保持時間Keep-Alive:timeout=20;max=20;)這個TCP通道,並採用伺服器和客戶端應答模式(Request-Response),不需要再創建一個鏈接通道,做到一個性能優化。

socket: 與HTTP協議不一樣,socket不是協議,他是在程序層面上對傳輸層協議(像TCP/IP)的介面封裝。我們知道傳輸層的協議,是解決數據在網路中傳輸的問題的,那麼socket(套接字)就是傳輸通道兩端的介面。

Websocket: WebSocket是包裝成了一個應用層協議作為socket,從而能夠讓客戶端和遠程服務端通過web建立全雙工通信。

WebSocket API 是HTML5 推出的東譽悶西。在客戶端我們可以通過HTML5 所提供的API 對websocket 進行創建、發送數據、監聽信息、監聽報錯等功能( HTML5 WebSocket )

我們知道WebSocket 是在Socket的基礎上實現的,所以我們要做的是對現有的Socket協議進行升級。

步驟: 客戶端發送websocket請求-->服務端接受並識別該請求-->對該請求協議進行升級--> 返回給客戶端 --> websocket 通道建立 --> 客戶端/服務端發送數據

協議升級

在這里需要注意的是頭部信息和頭部信息中的Sec-Websocket-Accept的值。

該值需要是一個通過base64加密的哈希值(sha1)。 而該加密所用的數據是客戶端傳過來的sec-websocket-key的值和MAGIC_STRINC內的固定值。 對MAGIC_STRINC的說明

Webscoket 中傳輸的數據是 數據幀(frame)

數據幀有多種類型 主要有:文本型、二進制數據

數據幀結構

每一列代表一個位元組,一個位元組8位,每一位又代表一個二進制數。

創建數據幀

解數據幀

心跳檢查

由於websocket 不進行交互會關閉通道所以,才有了心跳檢查。

websocket與和他http的區別

基於node實現websocket協議

使用nodeJS在HTTP上實現WebSocket

如何讓我的伺服器返回正確的Sec-WebSocket-Accept標頭值

學習WebSocket協議—從頂層到底層的實現原理

websocket 協議幀 解析

nodejs實現Websocket的數據接收發送

閱讀全文

與nodejswebsocket性能相關的資料

熱點內容
u盤文件給刪了 瀏覽:737
vuejsoauth2 瀏覽:78
2017微信支付日 瀏覽:81
機械臂編程如何開發 瀏覽:21
標書U盤PDF文件要不要簽字 瀏覽:222
ps軟體文件復制到d盤 瀏覽:148
一般工業固廢招標文件內容 瀏覽:583
網站建設報價怎麼算 瀏覽:66
三星a7000升級安卓502 瀏覽:486
word2010清除分隔符 瀏覽:781
樂視怎麼切換網路 瀏覽:425
cad列印pdf文件名稱與cad不一致 瀏覽:815
電氣與可編程式控制制是什麼 瀏覽:67
文件名中允許使用英文豎線嗎 瀏覽:531
編程貓在哪裡 瀏覽:775
win8共享文件夾訪問許可權 瀏覽:380
cad文件顯示為只讀不能保存怎麼辦 瀏覽:703
如何在系統里忘記網路 瀏覽:24
中小企業出口數據在哪裡找 瀏覽:715
win8和xp傳文件 瀏覽:75

友情鏈接