WebSocket 是一個持久化的協議,通過第一次 HTTP Request 建立連接之后,再把通信協議升級成 websocket,保持連接狀態,后續的數據交換不需要再重復請求。websocket 可以看成一種類似 TCP/IP 的 socke t技術,在 web 應用中實現、并獲得同 TCP/IP 通信一樣的雙向通信功能,因此客戶端既和服務器可以發送消息也可以接收消息,同時還支持多路復用的功能,由于它借用了 HTTP 協議的一些概念,所以被稱為 WebSocket。
webSocket API定義了web應用和服務器進行通信的公共接口,具體的構造函數創建對象、對象的屬性、方法、事件及它的意義,在上一篇《HTML5(十一)——WebSocket 基礎教程》文章中已詳細介紹。
WebSocket 協議可分為兩部分:握手階段和數據通信階段。
WebSocket 為應用層協議,定義在 TCP/IP 協議棧之上,連接服務器的 url 是以 ws 或 wss 開頭的。ws 開頭的默認TCP端口為80,wss 開頭的默認端口為443。
ws(websocket)是不安全的,容易被竊聽,只要別人知道你的ip和端口號,任何人都可以去連接通訊。
wss(web socket secure)是websocket的加密版本。
2.1、建立連接
客戶端去與服務器建立 TCP 連接,客戶端生成 websocket 對象,然后使用 API 建立連接,代碼如下:
let ws=new WebSocket('ws://localhost:8888')
ws.onopen=function(){
console.log("連接")
}
2.2、握手階段
客戶端與服務器建立連接之后,客戶端發送握手請求,隨后服務器發送握手響應即完成握手階段。
客戶端握手請求如下:
'GET / HTTP/1.1',
'Host: localhost:8888',
'Connection: Upgrade',
'Pragma: no-cache',
'Cache-Control: no-cache',
'Upgrade: websocket',
'Origin: file://',
'Sec-WebSocket-Version: 13',
'Accept-Encoding: gzip, deflate, br',
'Accept-Language: zh-CN,zh;q=0.9',
'Sec-WebSocket-Key: In1aAp/ya9Lkv+tsUtXLXQ==',
'Sec-WebSocket-Extensions: permessage-deflate; client_max_window_bits',
服務器握手響應如下:
Status Code: 101 Switching Protocols
Connection: Upgrade
sec-websocket-Accept: HBMDBbZMiS59r3aAITpGtJ64Mfc=Upgrade: websocket
2.3、數據通訊
WebSocket 握手連接成功之后。可以使用 send 進行發送數據,onmessage 接收數據,如下發送“你好”:
let ws=new WebSocket('ws://localhost:8888')
ws.onopen=function(){
console.log("連接成功")
ws.send("你好")
}
ws.onmessage=function(res){
console.log('接收到的消息',res)
}
服務器打印接收到的數據,如:<Buffer 81 86 af 87 53 b4 4b 3a f3 51 0a 3a>。
websocket 在發送數據時,被組織為一串數據幀,然后進行發送。傳送的幀包含兩部分:數據幀和控制幀。數據幀可以攜帶文本數據或者二進制數據,控制幀包含關閉幀和 Ping/Pong 幀。
把接收到的buffer十六進制數據轉成二進制數據,控制幀與上述各個類型幀進行對比解析其意義。
2.4、關閉連接
任何一端可以關閉連接。客戶端關閉連接如下:
ws.close()
然后發送關閉幀給對方,通常會帶有關閉連接的狀態碼,常見的狀態碼如下:
3.1、客戶端創建websocket對象,并建立連接之后發送數據。其中 ws 地址根據后臺服務端口對應。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script>
let ws=new WebSocket('ws://localhost:8888')
ws.onopen=function(){
console.log("連接")
ws.send("你好")
}
ws.onmessage=function(res){
console.log('res',res)
}
</script>
</body>
</html>
3.2、使用 node.js 創建一個 websocket 服務,如創建一個serve.js文件,代碼如下:
const http=require("http")
const net=require("net") //原生的websocket
const crypto=require('crypto') // 安全性校驗
let serve=net.createServer(sock=>{
//只握手一次
sock.once('data',(data)=>{
console.log("hand shake start") // 開始握手
let str=data.toString();
let lines=str.split('\r\n')
//舍棄第一行和最后兩行
lines=lines.slice(1,lines.length-2)
let headers={}
lines.forEach(line=>{
let [key,val]=line.split(': ')
headers[key.toLowerCase()]=val
})
if( headers['upgrade']!='websocket' ){
console.log("其他協議")
sock.end()
}else if(headers['sec-websocket-version']!=13){
console.log("版本不對")
sock.end()
}else{
let key=headers['sec-websocket-key']
let mask='258EAFA5-E914-47DA-95CA-C5AB0DC85B11'
//sha1(key+mask) -> base64=>client
let hash=crypto.createHash('sha1')
hash.update(key+mask)
let key2=hash.digest('base64')
sock.write(`HTTP/1.1 101 Switching Protocols\r\nUpgrade:websocket\r\nConnection:Upgrade\r\nsec-websocket-Accept:${key2}\r\n\r\n` )
console.log("hand shake end") // 握手結束
//真正的數據
sock.on('data',res=>{
console.log("真正接收數據",res)
//數據解析
let FIN=res[0]&0x001;
let opcode=data[0]&0x0F0;
let msak=data[1]&0x001;
let payload=data[1]&0x0FE;
})
}
})
//斷開
sock.on('end',()=>{
console.log("連接已斷開")
})
})
serve.listen("8888")
使用命令 node serve.js 或node serve 啟動服務,服務啟動成功之后可以使用localhost:8888訪問服務。
啟動服務之后,訪問前邊創建的html文件訪問websocket服務。
ebSocket 是 HTML5 開始提供的一種在單個 TCP 連接上進行全雙工通訊的協議。
WebSocket 使得客戶端和服務器之間的數據交換變得更加簡單,允許服務端主動向客戶端推送數據。在 WebSocket API 中,瀏覽器和服務器只需要完成一次握手,兩者之間就直接可以創建持久性的連接,并進行雙向數據傳輸。可以說WebSocket的出現,使得瀏覽器具備了實時雙向通信的能力
在 WebSocket API 中,瀏覽器和服務器只需要做一個握手的動作,然后,瀏覽器和服務器之間就形成了一條快速通道。兩者之間就直接可以數據互相傳送。
原來為了實現推送,很多公司用的是Ajax 輪詢,即按照特定的時間間隔,由瀏覽器對服務器發出HTTP請求,然后由服務器返回最新的數據給客戶端的瀏覽器。這種傳統的模式帶來很明顯的缺點,即瀏覽器需要不斷的向服務器發出請求,然而HTTP請求可能包含較長的頭部,其中真正有效的數據可能只是很小的一部分,顯然這樣會浪費很多的帶寬等資源。而websocket就可以解決這些問題。websocker有如下優點
HTML5 定義的 WebSocket 協議,能更好的節省服務器資源和帶寬,并且能夠更實時地進行通訊。
websocket在越來越多的公司開始開始使用,如推送,客服聊天之類的,今天推薦一個在線調試工具,使用起來超級方便,如果有更好的工具歡迎大家在下方留言,地址為:http://www.websocket-test.com/
還有并發壓力測試工具等一系列實用功能
ebSocket 和 Socket 的區別就像Java和JavaScript,并沒有什么太大的關系,但又不能說完全沒關系。可以這么說:
Java和JavaScript的關系
當我們探討兩件事物的區別和聯系時,我們想探討些什么?
對這個問題最直接的解決方法應該是去了解Socket和WebSocket的來源和用法,那么它們的區別和聯系就不言自明了。
Socket可以有很多意思,和IT較相關的本意大致是指在端到端的一個連接中,這兩個端叫做Socket。對于IT從業者來說,它往往指的是TCP/IP網絡環境中的兩個連接端,大多數的API提供者(如操作系統,JDK)往往會提供基于這種概念的接口,所以對于開發者來說也往往是在說一種編程概念。同時,操作系統中進程間通信也有Socket的概念,但這個Socket就不是基于網絡傳輸層的協議了。
操作系統中也有使用到Socket這個概念用來進行進程間通信,它和通常說的基于TCP/IP的Socket概念十分相似,代表了在操作系統中傳輸數據的兩方,只是它不再基于網絡協議,而是操作系統本身的文件系統。
通常所說的Socket API,是指操作系統中(也可能不是操作系統)提供的對于傳輸層(TCP/UDP)抽象的接口。現行的Socket API大致都是遵循了BSD Socket規范(包括Windows)。這里稱規范其實不太準確,規范其實是POSIX,但BSD Unix中對于Socket的實現被廣為使用,所以成為了實際的規范。如果你要使用HTTP來構建服務,那么就不需要關心Socket,如果你想基于TCP/IP來構建服務,那么Socket可能就是你會接觸到的API。
在TCP/IP網絡中HTTP的位置
從上圖中可以看到,HTTP是基于傳輸層的TCP協議的,而Socket API也是,所以只是從使用上說,可以認為Socket和HTTP類似(但一個是成文的互聯網協議,一個是一直沿用的一種編程概念),是對于傳輸層協議的另一種直接使用,因為按照設計,網絡對用戶的接口都應該在應用層。
和很多其他Internet上的事物一樣,Socket這個名稱來自于大名鼎鼎的ARPANET(Advanced Research Projects Agency),早期ARPANET中的Socket指的是一個源或者目的地址——大致就是今天我們所說的IP地址和端口號。最早的時候一個Socket指的是一個40位的數字(RFC33中說明了此用法,但在RFC36中并沒有明確地說使用40位數字來標識一個地址),其中前32為指向的地址(socket number,大致相當于IP),后8位為發送數據的源(link,大致相當于端口號)。對他們的叫法有很多的版本,這里列舉的并不嚴謹。
領取C++音視頻開發學習資料:點擊→音視頻開發(資料文檔+視頻教程+面試題)(FFmpeg+WebRTC+RTMP+RTSP+HLS+RTP)
隨著ARPANET的發展,后來(RFC433,Socket Number List)socket number被明確地定義為一個40位的數字,其中后8位被用來制定某個特定的應用使用(比如1是Telnet)。這8位數有很多名字:link、socket name、AEN(another eight number,看到這個名字我也是醉了),工程師逗逼起來也是挺拼的。
后來在Internet的規范制定中,才真正的用起了port number這個詞。至于為什么端口號是16位的,我想可能有兩個原因,一是對于當時的工程師來說,如果每個端口號來標識一個程序,65535個端口號也差不多夠用了。二可能是為了對齊吧,^_^!!。
在上邊提到的歷史中使用到的Socket,包括TCP文檔中使用到的Socket,其實指的是網絡傳輸中的一端,是一個虛擬化的概念。
上邊簡單敘述了Socket的意義,由于年代久遠,很多事情也搞不了那么清楚。但WebSocket是一個很晚近的東西,可以讓我們看到它是如何成為現在我們看到的這個樣子的。
關于HTML5的故事很多人都是知道的,w3c放棄了HTML,然后有一群人(也有說是這些人供職的公司,不過官方的文檔上是說的個人)創立了WHATWG組織來推動HTML語言的繼續發展,同時,他們還發展了很多關于Web的技術標準,這些標準不斷地被官方所接受。WebSocket就屬于WHATWG發布的Web Application的一部分(即HTML5)的產物。
大約在08年的時候,WG的工程師在討論網絡環境中需要一種全雙工的連接形式,剛開始一直叫做「TCPConnection」,并討論了這種協議需要支持的功能,大致已經和我們今天看到的WebSocket差不多了。他們認為基于現有的HTTP之上的一些技術(如長輪詢、Comet)并滿足不了這種需求,有必要定義一個全新的協議。
在很多的關于HTML5或者WebSocket的文檔中,都能看到一個名字,Hixie(Ian Hickson),他是WHATWG組織的發言人,曾供職于Netscape、Opera、Google,看工作的公司就知道這個人的背景了。
08年6月18日,一群WHATWG的工程師在討論一些技術問題,一個工程師提到說「我們之前討論的那個東西,不要叫TCPConnection 了,還是起個別的名字吧 」,接著幾個名字被提及,DuplexConnection,TCPSocket,SocketConnection ,一個叫mcarter(Michael Carter )的工程師說他馬上要寫一篇關于Comet的文章,如果可以確定這個名稱,想在文章中引用這個名字。
Socket一直以來都被人用來表示網絡中一個連接的兩端,考慮到怎么讓工程師更容易接受,后來Hixie說了一句「我看WebSocket這個名字就很適合嘛(Hixie briefly pops back online to record that “WebSocket” would probably be a good new name for the TCPConnection object)」,大家都沒有異議,緊接著mcarter在Comet Daily中發表了文章Independence Day: HTML5 WebSocket Liberates Comet From Hacks,后來隨著各大瀏覽器對WebSocket的支持,它變成了實際的標準,IETF也沿用了這個名字。
下邊是在WHATWG文檔中對WebSocket接口的定義
enum BinaryType { "blob", "arraybuffer" };
[Constructor(USVString url, optional (DOMString or sequence<DOMString>) protocols=[]), Exposed=(Window,Worker)]
interface WebSocket : EventTarget {
readonly attribute USVString url;
// ready state
const unsigned short CONNECTING=0;
const unsigned short OPEN=1;
const unsigned short CLOSING=2;
const unsigned short CLOSED=3;
readonly attribute unsigned short readyState;
readonly attribute unsigned long long bufferedAmount;
// networking
attribute EventHandler onopen;
attribute EventHandler onerror;
attribute EventHandler onclose;
readonly attribute DOMString extensions;
readonly attribute DOMString protocol;
void close([Clamp] optional unsigned short code, optional USVString reason);
// messaging
attribute EventHandler onmessage;
attribute BinaryType binaryType;
void send(USVString data);
void send(Blob data);
void send(ArrayBuffer data);
void send(ArrayBufferView data);
};
大多數新技術的出現都是建立在已有技術的鋪墊之上的,WebSocket內容的確定也是如此,其中就有Comet看不到的貢獻,Comet是一個很有趣的技術,有興趣可以看看這里
可以把WebSocket想象成HTTP,HTTP和Socket什么關系,WebSocket和Socket就是什么關系。
*請認真填寫需求信息,我們會在24小時內與您取得聯系。