websocket的原理

首先明確幾點:php

1.  websocket是一種協議。是html5的一種新協議;
2.  與http的區別是,它是一種雙向通訊協議,服務器和客戶端都能主動向對方發送或接受數據;
3.  websocket須要進行握手鍊接以後才能進行通訊;

由此咱們便知道了websocket誕生的意義:html

傳統的HTTP請求方式,是客戶端發送請求服務端響應的形式,這種方式在應對信息變化不是很頻繁的應用時還能較好的應對,可是對於實時要求/海量併發的應用來講顯得不是很給力,尤爲在當前業界移動的互聯網蓬勃發展的趨勢下,高併發與用戶實時響應是web應用常常面臨的問題,好比金融證券的實時信息,web導航應用中的地理位置獲取,社交網絡的實時消息推送等.html5

基於HTTP,咱們也有實時通信的方案,好比輪詢,好比使用flash的socket,可是這些都是非標準的,並且代價較大,HTML5的標準化組織須要針對實時通信的應用場景提供一套業界贊成的規範,那就是websocket.linux

socket通訊方式

 

socket的概念最初出如今linux網絡編程中,socket是在應用層和傳輸層之間的一個抽象層,他把TCP鏈接創建和釋放的過程驚醒了一層封裝,提供出接口給應用層.web

socket是如何與對應網絡中的進程進行通訊的呢?編程

咱們知道兩個進程若是須要進行通信最基本的一個前提能可以惟一的標示一個進程,在本地進程通信中咱們可使用PID來惟一標示一個進程,但PID只在本地惟一,網絡中的兩個進程PID衝突概率很大,這是後咱們須要另闢蹊徑了,咱們知道ip層的IP地址能夠惟一標示主機,而TCP層協議和端口能夠惟一標示一個進程,這樣咱們能夠利用IP地址+協議+端口號惟一標示網絡中的一個進程.瀏覽器

那麼在進行socket接口調用了時候,傳遞上述惟一標示以後,生成響應的協議請求,雙向通訊就能經過socket進行了.安全

websocket的做用也是模仿socket的通訊能力,可是不一樣的是,它自己是一種協議,瀏覽器和服務端會對這種協議進行解析,他是基於基礎TCP鏈接的,因此自己它是具備創建雙向鏈接的能力的,只不過具體的數據傳輸方式和一些針對web實時通信的特性須要在websocket中進行定義.服務器

websocket簡介

傳統的HTTP請求方式:websocket

websocket的通訊方式

能夠看到,HTTP請求每次與服務端交互必定的從新創建鏈接,可是websocket協議之須要雙方經過握手創建鏈接,在鏈接釋放以前,客戶端和服務端能夠相互接受和傳遞數據,由於雙方經過協議是知道創建websocket鏈接各自進程的端口號.

在客戶端中,咱們須要使用websocket對象,去鏈接ws://這樣的服務端url,這樣客戶端就會自動解析並識別websocket請求從而和服務端經過握手創建鏈接.

websocket客戶端鏈接報文

GET /webfin/websocket/ HTTP/1.1
Host: localhost
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: xqBt3ImNzJbYqRINxEFlkg==
Origin: http://localhost:8080
Sec-WebSocket-Version: 13

 

其中,"Sec-WebSocket-Key"是websocket客戶端發送的一個base64編碼的密文,要求服務端必須返回一個對應加密的"Sec-Socket-Accept"應答,不然客戶端會拋出"Error during WebSocket handshake"錯誤,並關閉鏈接.

websocket服務端響應報文

HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: K7DJLdLooIwIG/MOpvWFB3y3FE8=

 

 "Sec-WebSocket-Accept"的值是服務端採用與客戶端一直的祕鑰計算出來後返回客戶端的,"HTTP/1.1 101 Switching Protocols"表示服務端接受WebSocket鏈接握手成功,後續就能夠進行TCP通信了.

websocket的實現

要實現websocket,必須客戶端和服務端都要支持這個協議.

Tomcat在新版本中提供了WebSocketServle對象,用於支持websocket,php確定也有本身的websocket封裝.

而在客戶端HTML5提佛那個了標準的websocket API ,demo實現以下:

var ws = new WebSocket(「ws://echo.websocket.org」);
ws.onopen = function(){ws.send(「Test!」); };
ws.onmessage = function(evt){console.log(evt.data);ws.close();};
ws.onclose = function(evt){console.log(「WebSocketClosed!」);};
ws.onerror = function(evt){console.log(「WebSocketError!」);};
ws.send("hello");//向服務器發送消息

 第一行代碼是在申請一個websocket對象,參數是須要鏈接的服務器端的地址,同HTTP協議開頭同樣,websocket協議的url使用ws://開頭,另外安全的websocket協議使用ws://開頭

第二行到第五行爲websocket對象註冊消息的處理函數,websocket對象一共支持四個消息 onopen, onmessage, onclose, onerror,有了這四個事件,咱們就能夠很容易很輕鬆駕馭websocket.

當Browser和websocketserver鏈接成功後,會觸發onopen消息,若是鏈接失敗,發送.接收數據失敗或者處理數據出現錯誤,browser會觸發onerror消息;當browser接收到websocketserver發送過來的數據時,就會觸發onmessage消息,參數evt中包含server傳輸過來的數據;當browser接收到websocket端發送的關閉鏈接請求時,就觸發onclose消息,咱們能夠看出全部的操做都是採用一步回調的方法觸發,這樣不會阻塞UI,能夠得到更快的響應時間,更好的用戶體驗.

websocket的加密與解密

加密:

import struct
msg_bytes = "the emperor has not been half-baked in the early days of the collapse of the road, today down three points, yizhou weakness, this serious crisis autumn".encode("utf8")
token = b"\x81" # + 數據長度/運算位 + mask/數據長度 + mask/數據 + 數據
length = len(msg_bytes)

if length < 126:
    token += struct.pack("B", length)
elif length == 126:
    token += struct.pack("!BH", 126, length)
else:
    token += struct.pack("!BQ", 127, length)

msg = token + msg_bytes

print(msg)

 解密:

#b'\x81\x89\xf3\x99\x81-\x15\x05\x01\xcbO\x1be\x97]'
#b'\x81\x85s\x92a\x10\x1b\xf7\r|\x1c'
#b'\x81\x83H\xc0x\xa6y\xf2K'

hashstr = b'\x81\x85s\x92a\x10\x1b\xf7\r|\x1c'
# b'\x81 \x85s \x92a\x10\x1b\xf7  \r|\x1c' <126
# \x85s = 5
# print(chushibiao[1],chushibiao[1]&127)
# print(chushibiao[2:4],chushibiao[4:8])
# 將第二個字節也就是 \x83 第9-16位 進行與127進行位運算
payload = hashstr[1] & 127
print(payload)
if payload == 127:
    extend_payload_len = hashstr[2:10]
    mask = hashstr[10:14]
    decoded = hashstr[14:]
# 當位運算結果等於127時,則第3-10個字節爲數據長度
# 第11-14字節爲mask 解密所需字符串
# 則數據爲第15字節至結尾

if payload == 126:
    extend_payload_len = hashstr[2:4]
    mask = hashstr[4:8]
    decoded = hashstr[8:]
# 當位運算結果等於126時,則第3-4個字節爲數據長度
# 第5-8字節爲mask 解密所需字符串
# 則數據爲第9字節至結尾


if payload <= 125:
    extend_payload_len = None
    mask = hashstr[2:6]
    decoded = hashstr[6:]

# 當位運算結果小於等於125時,則這個數字就是數據的長度
# 第3-6字節爲mask 解密所需字符串
# 則數據爲第7字節至結尾

str_byte = bytearray()
# b'\x81 \x85s \x92a\x10\x1b \xf7\r|\x1c' <126
for i in range(len(decoded)): # 0  \xf7 ^ \x92a 1 \r ^ \x10 \x1c ^ \x1b
    byte = decoded[i] ^ mask[i % 4]
    str_byte.append(byte)

print(str_byte.decode("utf8"))
相關文章
相關標籤/搜索