什么是web應用,那些很重要,但是不常用的技術,websocket

 2023-10-17 阅读 28 评论 0

摘要:目錄 1. 為什么會有websocket 2. websocket協議格式 3. 協議具體實現 一、為什么需要 WebSocket? 初次接觸 WebSocket 的人,都會問同樣的問題:我們已經有了 HTTP 協議,為什么還需要另一個協議?它能帶來什么好處? 答案很簡單,

目錄

1. 為什么會有websocket

2. websocket協議格式

3. 協議具體實現

一、為什么需要 WebSocket?

初次接觸 WebSocket 的人,都會問同樣的問題:我們已經有了 HTTP 協議,為什么還需要另一個協議?它能帶來什么好處?

答案很簡單,因為 HTTP 協議有一個缺陷:通信只能由客戶端發起

什么是web應用、舉例來說,我們想了解今天的天氣,只能是客戶端向服務器發出請求,服務器返回查詢結果。HTTP協議做不到服務器主動向客戶端推送信息。

這種單向請求的特點,注定了如果服務器有連續的狀態變化,客戶端要獲知就非常麻煩。我們只能使用"輪詢":每隔一段時候,就發出一個詢問,了解服務器有沒有新的信息。最典型的場景就是聊天室。

輪詢的效率低,非常浪費資源(因為必須不停連接,或者HTTP連接始終打開)。因此,工程師們一直在思考,有沒有更好的方法。WebSocket就是這樣發明的

WebSocket協議在2008年誕生,2011年成為國際標準。所有瀏覽器都已經支持了。

它的最大特點就是,服務器可以主動向客戶端推送信息,客戶端也可以主動向服務器發送信息,是真正的雙向平等對話,屬于服務器推送技術的一種。

其他特點包括:

websocket還沒準備好、1)建立在 TCP 協議之上,服務器端的實現比較容易。

(2)與 HTTP 協議有著良好的兼容性。默認端口也是80和443,并且握手階段采用 HTTP 協議,因此握手時不容易屏蔽,能通過各種 HTTP 代理服務器。

(3)數據格式比較輕量,性能開銷小,通信高效。

(4)可以發送文本,也可以發送二進制數據。

(5)沒有同源限制,客戶端可以與任意服務器通信。

(6)協議標識符是ws(如果加密,則為wss),服務器網址就是 URL。

websocket協議格式

html5 websocket?Browser已經支持http協議,為什么還要開發一種新的WebSocket協議呢?我們知道http協議是一種單向的網絡協議,在建立連接后,它只允許Browser/UA(UserAgent)向WebServer發出請求資源后,WebServer才能返回相應的數據。而WebServer不能主動的推送數據給Browser/UA,當初這么設計http協議也是有原因的,假設WebServer能主動的推送數據給Browser/UA,那Browser/UA就太容易受到攻擊,一些廣告商也會主動的把一些廣告信息在不經意間強行的傳輸給客戶端,這不能不說是一個災難。那么單向的http協議給現在的網站或Web應用程序開發帶來了哪些問題呢?

  • 一條連接上只可以發送一個請求
  • 請求只能從客戶端開始。客戶端不可以接收除了響應以外的指令。
  • 請求 / 響應首部未經過壓縮就直接進行傳輸。首部的信息越多,那么延遲就越大。
  • 發送冗長的首部。每次互相發送相同的首部造成的浪費越多
  • 可以任意選擇數據壓縮格式。非強制壓縮發送

ajax輪詢

ajax(異步的javascript與xml技術)是一種有效利用javascript和dom的操作,以達到局部web頁面的提花和加載的異步通信手段。和以前的同步通信相比,他只更新一部分頁面,相應中傳輸餓數據量會因此的減少。

ajax輪詢的原理是,讓瀏覽器每隔一段時間就發送一次請求,詢問服務器是否有新消息。
而利用ajax實時的從服務器獲取內容,有可能導致大量的請求產生。

那些很重要,但是不常用的技術,websocket

?

長輪詢

原理和ajax輪詢差不多,都是采用輪詢的方式,不過采用的是阻塞模型。也就是說,當客戶端發起連接后,如果服務器端內容沒有更新,將響應至于掛起狀態,一直不回復response給客戶端,知道有內容更新,再返回響應。

nodejs websocket?那些很重要,但是不常用的技術,websocket

?


雖然可以做到實時更新,但是為了保留響應,一次連接餓持續時間也變長了。期間,為了維持連接會消費更多的資源。

從上面兩種方式中,其實可以看出是再不斷的建立http連接,然后等待服務器處理,可以體現出了http的特點:被動性,即:請求只能由客戶端發起。服務器端不能主動聯系客戶端。
不管怎么樣,上面這兩種都是非常消耗資源的。
ajax輪詢 需要服務器有很快的處理速度和資源。(速度)
長輪詢 需要有很高的并發,也就是說同時接待客戶的能力。(場地大小)

除了以上這些,HTTP還是一個無狀態協議。
通俗的說就是,服務器因為每天要接待太多瀏覽器了,是個健忘鬼,你一斷連接,他就把你的東西全忘光了,把你的東西全丟掉了。你第二次還得再告訴服務器一遍。

WebSocket

WebSocket其實是HTTP協議上的一種補充,他們有交集但并不是全部。

websocket 支持。那些很重要,但是不常用的技術,websocket

?

一旦web服務器和客戶端建立起websocket協議的通信連接,之后所有的通信都依靠這個專用連接進行。只需要經過一次HTTP請求,就可以做到源源不斷的信息傳送了。

那些很重要,但是不常用的技術,websocket

?

websocket是基于HTTP協議的,或者說借用了http的協議來完成一部分握手。為了實現websocket通信,在http建立連接后,還需要進行一次“握手”的步驟。

握手 · 請求

GET /chat HTTP/1.1 Host: server.example.com Upgrade: websocket Connection: Upgrade Sec-WebSocket-Key: x3JJHMbDL1EzLkh9GBhXDw== Sec-WebSocket-Protocol: chat, superchat Sec-WebSocket-Version: 13 Origin: http://example.com

websocket的弊端?為了實現websocket通信,需要用到http的Upgrade首部字段,告知服務器通信協議已發生改變:我要發起的是websocket協議。以達到握手的目的。
Sec-WebSocket-Key字段記錄著握手必不可少的鍵值,用于驗證服務器是否支持websocket通信。
Sec-WebSocket-Protocol字段記錄的是所需要使用的協議。

握手 · 響應

HTTP/1.1 101 Switching Protocols Upgrade: websocket Connection: Upgrade Sec-WebSocket-Accept: HSmrc0sMlYUkAGmm5OPpG2HaGWk= Sec-WebSocket-Protocol: chat

對于客戶端的請求,服務器返回狀態碼 101 Switching Protocols的響應。
返回Upgrate告訴客戶端即將升級的協議是Websocket協議。
Sec-WebSocket-Accept字段值是由握手請求中的Sec-WebSocket-Key字段值加密過后生成的。
Sec-WebSocket-Protocol 則是表明最總使用的協議。

到這里,http已經完成所有他的工作了,接下來通信時不再使用HTTP的數據幀,而是使用websocket獨立的數據幀。

那些很重要,但是不常用的技術,websocket

?

因此,websocket協議具有以下的特點:

  • 推送功能
    支持服務器端向客戶端推送功能。服務器可以直接發送數據而不用等待客戶端的請求。
  • 減少通信量
    只要建立起websocket連接,就一直保持連接,在此期間可以源源不斷的傳送消息,直到關閉請求。也就避免了HTTP的非狀態性。
  • 減少資源消耗
    那么為什么他會解決服務器上消耗資源的問題呢?其實我們所用的程序是要經過兩層代理的,即HTTP協議在Nginx等服務器的解析下,然后再傳送給相應的Handler(PHP等)來處理。簡單地說,我們有一個非常快速的接線員(Nginx),他負責把問題轉交給相應的客服(Handler)。本身接線員基本上速度是足夠的,但是每次都卡在客服(Handler)了,老有客服處理速度太慢。導致客服不夠。Websocket就解決了這樣一個難題,建立后,可以直接跟接線員建立持久連接,有信息的時候客服想辦法通知接線員,然后接線員在統一轉交給客戶。這樣就可以解決客服處理速度過慢的問題了。

協議具體實現

前提:

c++ websocket?本人最近做的項目,服務器端用的是C++寫的,而與客戶端交互用的是websocket,服務器端要想正常的使用數據,必須要對websocket協議進行解析。

WebSocket數據格式

那些很重要,但是不常用的技術,websocket

?

  1. FIN:表示這個數據是不是接收完畢,為1表示收到的數據是完整的,占1bit
  2. RSV1~3:用于擴展,通常都為0,各占1bit
  3. OPCODE:表示報文的類型,占4bit 0x00:標識一個中間數據包0x01:標識一個text數據包0x02:標識一個二進制數據包0x03~07:保留0x08:標識一個斷開連接數據包0x09:標識一個ping數據包0x0A:標識一個pong數據包0x0B~F:保留
  4. MASK:用于表示數據是否經常掩碼處理,為1時,Masking-key即存在,占1bit
  5. Payload len:表示數據長度,即Payload Data的長度,當Payload len為0~125時,表示的值就是Payload Data的真實長度;當Payload len為126時,報文其后的2個字節形成的16bits無符號整型數的值是Payload Data的真實長度(網絡字節序,需轉換);當Payload len為127時,報文其后的8個字節形成的64bits無符號整型數的值是Payload Data的真實長度(網絡字節序,需轉換);
  6. Masking-key:掩碼,當Mask為1時存在,占4字節32bit
  7. Payload Data:表示數據

C++對websocket協議處理

/**
* @brief getWSFrameData 解析websocket的協議包,不能解決粘包半包問題
* @param msg 待解析的數據
* @param msgLen 待解析的數據長度
* @param outBuf 解析完成數據
* @return
*/
int unPackingWSFrameData(char *msg,
int msgLen,
std::vector<char> &outBuf)
{
//報文長度一定大于2字節,對于小于的,做返回處理
if(msgLen < 2)
{
return -3;
}uint8_t opcode_ = 0;
uint8_t mask_ = 0;
uint8_t masking_key_[4] = {0,0,0,0};
uint64_t payload_length_ = 0;
int pos = 0;//Opcode
opcode_ = msg[pos] & 0x0f;
pos++;
//MASK
mask_ = (unsigned char)msg[pos] >> 7;
//Payload length
payload_length_ = msg[pos] & 0x7f;
pos++;
if(payload_length_ == 126)
{
uint16_t length = 0;
memcpy(&length, msg + pos, 2);
pos += 2;
payload_length_ = ntohs(length);
}
else if(payload_length_ == 127)
{
uint32_t length = 0;
memcpy(&length, msg + pos, 8);
pos += 8;
payload_length_ = ntohl(length);
}
//Masking-key
if(mask_ == 1)
{
for(int i = 0; i < 4; i++)
{
masking_key_[i] = msg[pos + i];
}
pos += 4;
}
//取出消息數據
if (msgLen >= pos + payload_length_ )
{
outBuf.clear();
if(mask_ != 1)
{
char* dataBegin = msg + pos;
outBuf.insert(outBuf.begin(), dataBegin, dataBegin+payload_length_);
}
else
{
for(uint i = 0; i < payload_length_; i++)
{
int j = i % 4;
outBuf.push_back(msg[pos + i] ^ masking_key_[j]);
}
}}
else
{
//此時包長小于報文中記錄的包長
return -2;
}//斷開連接類型數據包
if ((int)opcode_ == 0x8)
return -1;return 0;
}

以上函數即實現了對收到websocket數據的解析,返回結果為:vector<char>output;

通常會在函數外面對此進行轉換為char*,方便我們使用,見下:

vector<char>output;
char* out = &output[0];

當然,現在的解析還不是完美的解決方法,因為在實際的使用當中,會存在接收的包粘包,半包等等問題,而以上函數只能解決收到包正好是一個完整的包的情況;具體解決粘包半包問題,留待下次博客吧!

參考資料:后臺私信“資料”送

websocket原理與機制,?

結尾:

只為記錄,只為分享! 愿所寫能對你有所幫助。不忘記點個贊,謝謝~

后臺私信可以領取 內容包括:C/C++,Linux,Nginx,ZeroMQ,MySQL,Redis,fastdfs,MongoDB,ZK,流媒體, WebRTC ,CDN,P2P,K8S,Docker,Golang, TCP/IP,MTK , 嵌入式 , 協程,DPDK等等 。

版权声明:本站所有资料均为网友推荐收集整理而来,仅供学习和研究交流使用。

原文链接:https://hbdhgg.com/1/142100.html

发表评论:

本站为非赢利网站,部分文章来源或改编自互联网及其他公众平台,主要目的在于分享信息,版权归原作者所有,内容仅供读者参考,如有侵权请联系我们删除!

Copyright © 2022 匯編語言學習筆記 Inc. 保留所有权利。

底部版权信息