HTML5定義了WebSocket協議,能更好的節省服務器資源和帶寬,而且可以更實時地進行通信。html
在2008年誕生,2011年成爲國際標準。python
如今基本全部瀏覽器都已經支持了。程序員
WebSocket是一種在單個TCP鏈接上進行全雙工通訊的協議。在WebSocket API中,瀏覽器和服務器只須要完成一次握手(不是指創建TCP鏈接的那個三次握手,是指在創建TCP鏈接後傳輸一次握手數據),二者之間就直接能夠建立持久性的鏈接,並進行雙向數據傳輸。web
Websocket使用ws或wss的統一資源標誌符,相似於HTTPS,其中wss表示在TLS之上的Websocket。如:後端
ws://example.com/wsapi wss://secure.example.com/
Websocket使用和 HTTP 相同的 TCP 端口,能夠繞過大多數防火牆的限制。默認狀況下,Websocket協議使用80端口;運行在TLS之上時,默認使用443端口。api
WebSocket 是獨立的、建立在 TCP 上的協議。 報文瀏覽器
Websocket 經過 HTTP/1.1 協議的101狀態碼進行握手。服務器
爲了建立Websocket鏈接,須要經過瀏覽器發出請求,以後服務器進行迴應,這個過程一般稱爲「握手」(handshaking)。websocket
一個典型的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/
Socket.IO 本是一個面向實時 web 應用的 JavaScript 庫,如今已成爲擁有衆多語言支持的Web即時通信應用的框架。
Socket.IO 主要使用WebSocket協議。可是若是須要的話,Socket.io能夠回退到幾種其它方法,例如Adobe Flash Sockets,JSONP拉取,或是傳統的AJAX拉取,而且在同時提供徹底相同的接口。儘管它能夠被用做WebSocket的包裝庫,它仍是提供了許多其它功能,好比廣播至多個套接字,存儲與不一樣客戶有關的數據,和異步IO操做。
Socket.IO 不等價於 WebSocket,WebSocket只是Socket.IO實現即時通信的其中一種技術依賴,並且Socket.IO還在實現WebSocket協議時作了一些調整。
Socket.IO 會自動選擇合適雙向通訊協議,僅僅須要程序員對套接字的概念有所瞭解。
有Python庫的實現,能夠在Python實現的Web應用中去實現IM後臺服務。
Socket.io並非一個基本的、獨立的、可以回退到其它實時協議的WebSocket庫,它其實是一個依賴於其它實時傳輸協議的自定義實時傳輸協議的實現。該協議的協商部分使得支持標準WebSocket的客戶端不能直接鏈接到Socket.io服務器,而且支持Socket.io的客戶端也不能與非Socket.io框架的WebSocket或Comet服務器通訊。於是,Socket.io要求客戶端與服務器端均須使用該框架。
pip install python-socketio
方式1
使用多進程多線程模式的WSGI服務器對接(如uWSGI、gunicorn)
import socketio # create a Socket.IO servers sio = socketio.Server() # 打包成WSGI應用,可使用WSGI服務器託管運行 app = socketio.WSGIApp(sio) # Flask Django
建立好app對象後,使用uWSGI、或gunicorn服務器運行此對象。
方式2
做爲Flask、Django 應用中的一部分
from wsgi import app # a Flask, Django, etc. application import socketio # create a Socket.IO server sio = socketio.Server() app = socketio.WSGIApp(sio, app)
建立好app對象後,使用uWSGI、或gunicorn服務器運行此對象。
方式3
使用協程的方式運行 (推薦)
import eventlet eventlet.monkey_patch() import socketio import eventlet.wsgi sio = socketio.Server(async_mode='eventlet') # 指明在evenlet模式下 app = socketio.Middleware(sio) eventlet.wsgi.server(eventlet.listen(('', 8000)), app)
由於服務器與客戶端進行即時通訊時,會盡量的使用長鏈接,因此若服務器採用多進程或多線程方式運行,受限於服務器能建立的進程或線程數,可以支持的併發鏈接客戶端不會很高,也就是服務器性能有限。採用協程方式運行服務器,能夠提高即時通訊服務器的性能。
不一樣於HTTP服務的編寫方式,SocketIO服務編寫再也不以請求Request和響應Response來處理,而是對收發的數據以消息(message)來對待,收發的不一樣類別的消息數據又以事件(event)來區分。
本來HTTP服務編寫中處理請求、構造響應的視圖處理函數在SocketIO服務中改成編寫收發不一樣事件的事件處理函數。
編寫事件處理方法,能夠接收指定的事件消息數據,並在處理方法中對消息數據進行處理。
@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
disconnect 爲特殊事件,當客戶端斷開鏈接後自動執行
connect、disconnect與自定義事件處理方法的函數傳入參數不一樣
羣發
sio.emit('my event', {'data': 'foobar'})
給指定用戶發送
sio.emit('my event', {'data': 'foobar'}, room=user_sid)
給一組用戶發送
SocketIO提供了房間(room)來爲客戶端分組
sio.enter_room(sid, room_name)
將鏈接的客戶端添加到一個room
@sio.on('chat') def begin_chat(sid): sio.enter_room(sid, 'chat_users')
注意:當客戶端鏈接後,socketio會自動將客戶端添加到以此客戶端sid爲名的room中
sio.leave_room(sid, room_name)
將客戶端從一個room中移除
@sio.on('exit_chat') def exit_chat(sid): sio.leave_room(sid, 'chat_users')
sio.rooms(sid)
查詢sid客戶端所在的全部房間
給一組用戶發送消息的示例
@sio.on('my message') def message(sid, data): sio.emit('my reply', data, room='chat_users')
也可在羣組發消息時跳過指定客戶端
@sio.on('my message') def message(sid, data): sio.emit('my reply', data, room='chat_users', skip_sid=sid)
使用send
發送message
事件消息
對於'message'事件,可使用send方法
sio.send({'data': 'foobar'}) sio.send({'data': 'foobar'}, room=user_sid)
import socketio sio = socketio.Client() @sio.on('connect') def on_connect(): pass @sio.on('event') def on_event(data): pass sio.connect('http://10.211.55.7:8000') sio.wait()