
WebSocket
HTML5定義了WebSocket協定,能更好的節省伺服器資源和帶寬,并且能夠更實時地進行通訊。
在2008年誕生,2011年成為國際标準。
現在基本所有浏覽器都已經支援了。
WebSocket是一種在單個TCP連接配接上進行全雙工通信的協定。在WebSocket API中,浏覽器和伺服器隻需要完成一次握手(不是指建立TCP連接配接的那個三次握手,是指在建立TCP連接配接後傳輸一次握手資料),兩者之間就直接可以建立持久性的連接配接,并進行雙向資料傳輸。
Websocket使用ws或wss的統一資源标志符,類似于HTTPS,其中wss表示在TLS之上的Websocket。如:
ws://example.com/wsapi
wss://secure.example.com/
Websocket使用和 HTTP 相同的 TCP 端口,可以繞過大多數防火牆的限制。預設情況下,Websocket協定使用80端口;運作在TLS之上時,預設使用443端口。
轉存失敗重新上傳取消
握手協定
WebSocket 是獨立的、建立在 TCP 上的協定。 封包
Websocket 通過 HTTP/1.1 協定的101狀态碼進行握手。
為了建立Websocket連接配接,需要通過浏覽器送出請求,之後伺服器進行回應,這個過程通常稱為“握手”(handshaking)。
一個典型的Websocket握手請求如下:
用戶端請求
GET / HTTP/1.1
Upgrade : websocket
Connection : Upgrade
Host : example.com
Origin : http://example.com
Sec-WebSocket-Key : sN9cRrP/n9NdMgdcy2VJFQ==
Sec-WebSocket-Version : 13
伺服器回應
HTTP/1.1 101 Switching Protocols
Upgrade : websocket
Connection : Upgrade
Sec-WebSocket-Accept : fFBooB7FAkLlXgRSz0BT3v4hq5s=
Sec-WebSocket-Location : ws://example.com/
- Connection必須設定Upgrade,表示用戶端希望連接配接更新。
- Upgrade字段必須設定Websocket,表示希望更新到Websocket協定。
- Sec-WebSocket-Key是随機的字元串,伺服器端會用這些資料來構造出一個SHA-1的資訊摘要。把“Sec-WebSocket-Key”加上一個特殊字元串“258EAFA5-E914-47DA-95CA-C5AB0DC85B11”,然後計算SHA-1摘要,之後進行BASE-64編碼,将結果做為“Sec-WebSocket-Accept”頭的值,傳回給用戶端。如此操作,可以盡量避免普通HTTP請求被誤認為Websocket協定。
- Sec-WebSocket-Version 表示支援的Websocket版本。RFC6455要求使用的版本是13,之前草案的版本均應當棄用。
- Origin字段是可選的,通常用來表示在浏覽器中發起此Websocket連接配接所在的頁面,類似于Referer。但是,與Referer不同的是,Origin隻包含了協定和主機名稱。
- 其他一些定義在HTTP協定中的字段,如Cookie等,也可以在Websocket中使用。
優點
- 較少的控制開銷。在連接配接建立後,伺服器和用戶端之間交換資料時,用于協定控制的資料標頭部相對較小。在不包含擴充的情況下,對于伺服器到用戶端的内容,此頭部大小隻有2至10位元組(和資料包長度有關);對于用戶端到伺服器的内容,此頭部還需要加上額外的4位元組的掩碼。相對于HTTP請求每次都要攜帶完整的頭部,此項開銷顯著減少了。
- 更強的實時性。由于協定是全雙工的,是以伺服器可以随時主動給用戶端下發資料。相對于HTTP請求需要等待用戶端發起請求服務端才能響應,延遲明顯更少;即使是和Comet等類似的長輪詢比較,其也能在短時間内更多次地傳遞資料。
- 保持連接配接狀态。與HTTP不同的是,Websocket需要先建立連接配接,這就使得其成為一種有狀态的協定,之後通信時可以省略部分狀态資訊。而HTTP請求可能需要在每個請求都攜帶狀态資訊(如身份認證等)。 更好的二進制支援。Websocket定義了二進制幀,相對HTTP,可以更輕松地處理二進制内容。
- 可以支援擴充。Websocket定義了擴充,使用者可以擴充協定、實作部分自定義的子協定。如部分浏覽器支援壓縮等。
- 更好的壓縮效果。相對于HTTP壓縮,Websocket在适當的擴充支援下,可以沿用之前内容的上下文,在傳遞類似的資料時,可以顯著地提高壓縮率。
- 沒有同源限制,用戶端可以與任意伺服器通信。
- 可以發送文本,也可以發送二進制資料。
Socket.IO
1 簡介
Socket.IO 本是一個面向實時 web 應用的 JavaScript 庫,現在已成為擁有衆多語言支援的Web即時通訊應用的架構。這個架構中同時提供了輪詢與websocket的通訊開發支援,隻要編寫一套前後代碼,可以适配任何浏覽器。
Socket.IO 主要使用WebSocket協定。但是如果需要的話,Socket.io可以回退到幾種其它方法,例如Adobe Flash Sockets,JSONP拉取,或是傳統的AJAX拉取,并且在同時提供完全相同的接口。盡管它可以被用作WebSocket的包裝庫,它還是提供了許多其它功能,比如廣播至多個套接字,存儲與不同客戶有關的資料,和異步IO操作。
Socket.IO 不等價于 WebSocket,WebSocket隻是Socket.IO實作即時通訊的其中一種技術依賴,而且Socket.IO還在實作WebSocket協定時做了一些調整。socketio在封裝websocket協定實作的時候,對websocket協定做了調整,是以不再相容原生的websocket,意味着前端與後端必須同時采用socketio來實作才能完成即時通訊。
優點:
Socket.IO 會自動選擇合适雙向通信協定,僅僅需要程式員對套接字的概念有所了解。
有Python庫的實作,可以在Python實作的Web應用中去實作IM背景服務。
缺點:
Socket.io并不是一個基本的、獨立的、能夠回退到其它實時協定的WebSocket庫,它實際上是一個依賴于其它實時傳輸協定的自定義實時傳輸協定的實作。該協定的協商部分使得支援标準WebSocket的用戶端不能直接連接配接到Socket.io伺服器,并且支援Socket.io的用戶端也不能與非Socket.io架構的WebSocket或Comet伺服器通信。因而,Socket.io要求用戶端與伺服器端均須使用該架構。
安裝
import eventlet
eventlet.monkey_patch() # 系統同名函數替換
- 方式1
使用多程序多線程模式的WSGI伺服器對接(如uWSGI、gunicorn)
# create a Socket.IO servers
# 打包成WSGI應用,可以使用WSGI伺服器托管運作
app = socketio.WSGIApp(sio) # Flask Django
建立好app對象後,使用uWSGI、或gunicorn伺服器運作此對象。
- 方式2
作為Flask、Django 應用中的一部分
from wsgi import app # a Flask, Django, etc. application
# create a Socket.IO server
建立好app對象後,使用uWSGI、或gunicorn伺服器運作此對象。
- 方式3
使用協程的方式運作 (推薦)
sio = socketio.Server(async_mode= 'eventlet' ) # 指明在evenlet模式下
說明
因為伺服器與用戶端進行即時通信時,會盡可能的使用長連接配接,是以若伺服器采用多程序或多線程方式運作,受限于伺服器能建立的程序或線程數,能夠支援的并發連接配接用戶端不會很高,也就是伺服器性能有限。采用協程方式運作伺服器,可以提升即時通信伺服器的性能。
事件處理
不同于HTTP服務的編寫方式,SocketIO服務編寫不再以請求Request和響應Response來處理,而是對收發的資料以消息(message)來對待,收發的不同類别的消息資料又以事件(event)來區分。
原本HTTP服務編寫中處理請求、構造響應的視圖處理函數在SocketIO服務中改為編寫收發不同僚件的事件處理函數。
1)事件處理方法
編寫事件處理方法,可以接收指定的事件消息資料,并在處理方法中對消息資料進行處理。
@sio.on('connect')
def on_connect(sid, environ):
"""
與用戶端建立好連接配接後被執行
:param sid: string sid是socketio為目前連接配接用戶端生成的識别id
:param environ: dict 在連接配接握手時用戶端發送的握手資料(HTTP封包解析之後的字典)
"""
pass
@sio.on('disconnect')
def on_disconnect(sid):
"""
與用戶端斷開連接配接後被執行
:param sid: string sid是斷開連接配接的用戶端id
"""
pass
# 以字元串的形式表示一個自定義事件,事件的定義由前後端約定
@sio.on('my custom event')
def my_custom_event(sid, data):
"""
自定義事件消息的處理方法
:param sid: string sid是發送此事件消息的用戶端id
:param data: data是用戶端發送的消息資料
"""
pass
注意
- connect 為特殊事件,當用戶端連接配接後自動執行
- disconnect 為特殊事件,當用戶端斷開連接配接後自動執行
- connect、disconnect與自定義事件處理方法的函數傳入參數不同
2)發送事件消息
- 群發
-
'my event'sio.emit(
'data', {
'foobar':
})
- 給指定使用者發送
-
'my event'sio.emit(
'data', {
'foobar':
}, room=user_sid)
- 給一組使用者發送
SocketIO提供了房間(room)來為用戶端分組
-
-
sio.enter_room(sid, room_name)
-
将連接配接的用戶端添加到一個room
@sio.on('chat')
def begin_chat(sid):
注意:當用戶端連接配接後,socketio會自動将用戶端添加到以此用戶端sid為名的room中
-
-
sio.leave_room(sid, room_name)
-
将用戶端從一個room中移除
@sio.on('exit_chat')
def exit_chat(sid):
-
-
sio.rooms(sid)
-
查詢sid用戶端所在的所有房間
給一組使用者發送消息的示例
@sio.on('my message')
def message(sid, data):
也可在群組發消息時跳過指定用戶端
@sio.on('my message')
def message(sid, data):
- 使用
發送send
事件消息message
對于'message'事件,可以使用send方法