目錄
一、WebSocket -網(wǎng)絡(luò)通信協(xié)議
1-1 簡(jiǎn)介
二,、Websockets servers and clients in Python
2-0 connect
2-0-1 建立一對(duì)一短連接 ?
2-0-2 建立一對(duì)一長連接
2-0-3 建立一對(duì)多長連接
2-1 asyncio
三、SocketIO
3-0 Flask-Sockets VS Flask-SocketIO
3-1 Socket.IO
3-2 python-socketio
3-2-0 安裝
3-2-1 服務(wù)端基本總結(jié) ?
3-2-3 一對(duì)多長連接
3-2 Flask-SocketIO
四、WebSocket for client - 單純用于連接的模塊
五,、Tornado - 一個(gè)支持HTTP和WebSocket的Web框架
六、Aiohttp - 基于asyncio,一個(gè)支持HTTP和WebSocket的框架
一,、WebSocket -網(wǎng)絡(luò)通信協(xié)議
WebSocket 教程 - 阮一峰
Web 基于 B/S 架構(gòu),,通常使用 HTTP 協(xié)議進(jìn)行通信,HTTP 本質(zhì)是一個(gè)單向請(qǐng)求,,若需要持續(xù)的獲取服務(wù)端的數(shù)據(jù)變化,,必須輪詢(polling)進(jìn)行數(shù)據(jù)請(qǐng)求【每隔一段時(shí)間發(fā)送request,服務(wù)器將新數(shù)據(jù)返回】,。
使用HTTP協(xié)議處理服務(wù)端數(shù)據(jù)監(jiān)控的弊端:輪詢效率低,,浪費(fèi)資源。因?yàn)楸仨毑煌=⑦B接,,或保持HTTP始終連接,。
wiki - 服務(wù)器推送方式
1-1 簡(jiǎn)介
為了解決上述的需求問題,并提高數(shù)據(jù)傳輸效率,,WebSocket 協(xié)議就出現(xiàn)了,。
WebSocket 協(xié)議下,服務(wù)端和客戶端能相互的主動(dòng)發(fā)送消息,,建立平等對(duì)話,。屬于服務(wù)器推送技術(shù)的一種。
WebSocket 一種在單個(gè) TCP 連接上進(jìn)行全雙工通訊的協(xié)議,。
WebSocket 是獨(dú)立的,、創(chuàng)建在 TCP 上的協(xié)議,和 HTTP 的唯一關(guān)聯(lián)是使用 HTTP 協(xié)議的101狀態(tài)碼進(jìn)行協(xié)議切換,,使用的 TCP 端口是80,,可以用于繞過大多數(shù)防火墻的限制。
WebSocket 使得客戶端和服務(wù)器之間的數(shù)據(jù)交換變得更加簡(jiǎn)單,,允許服務(wù)端直接向客戶端推送數(shù)據(jù)而不需要客戶端進(jìn)行請(qǐng)求,,在 WebSocket API 中,瀏覽器和服務(wù)器只需要完成一次握手,,兩者之間就直接可以創(chuàng)建持久性的連接,,并允許數(shù)據(jù)進(jìn)行雙向傳送。
二,、Websockets servers and clients in Python
websocket github
它構(gòu)建于asyncio(協(xié)程)Python的標(biāo)準(zhǔn)異步I / O框架之上,,提供了一個(gè)優(yōu)雅的基于協(xié)程的API。
即,,使用 asyncio 包裹的基本python實(shí)現(xiàn)方式
2-0 connect
2-0-1 建立一對(duì)一短連接 ?
# ----------- server 端 ----------- async def hello(websocket, path): print('-------- hello opened ------') name = await websocket.recv() greeting = 'hello %s' % name await websocket.send(greeting) start_server = websockets.serve(hello, 'localhost', 8765) asyncio.get_event_loop().run_until_complete(start_server) asyncio.get_event_loop().run_forever() # ----------- client 端 ----------- async with websockets.connect( 'ws://localhost:8765') as websocket: name = input('your name:') await websocket.send(name) greeting = await websocket.recv() asyncio.get_event_loop().run_until_complete(hello())
2-0-2 建立一對(duì)一長連接
# ----------- server 端 ----------- async def producer_handler(websocket, path): print('---- 建立了連接 -----') message = input('please input:') await websocket.send(message) start_server = websockets.serve(producer_handler, 'localhost', 8765) asyncio.get_event_loop().run_until_complete(start_server) asyncio.get_event_loop().run_forever() # ----------- client 端 ----------- async def consumer_handler(): async with websockets.connect( 'ws://localhost:8765') as websocket: async for message in websocket: asyncio.get_event_loop().run_until_complete(consumer_handler())
2-0-3 建立一對(duì)多長連接
# ----------- server 端 ----------- async def notify_users(): # 對(duì)注冊(cè)列表內(nèi)的客戶端進(jìn)行推送 if USERS: # asyncio.wait doesn't accept an empty list message = input('please input:') await asyncio.wait([user.send(message) for user in USERS]) async def register(websocket): async def unregister(websocket): async def counter(websocket, path): # register(websocket) sends user_event() to websocket await register(websocket) # 處理客戶端數(shù)據(jù)請(qǐng)求 (業(yè)務(wù)邏輯) async for message in websocket: await unregister(websocket) asyncio.get_event_loop().run_until_complete( websockets.serve(counter, 'localhost', 6789)) asyncio.get_event_loop().run_forever() # ----------- client 端 ----------- async def consumer_handler(): async with websockets.connect( 'ws://localhost:6789') as websocket: async for message in websocket: asyncio.get_event_loop().run_until_complete(consumer_handler())
2-1 asyncio
asyncio -- 異步 I/O
三,、SocketIO
python使用websocket的幾種方式
3-0 Flask-Sockets VS Flask-SocketIO
- Flask-Sockets
- Flask-Sockets 只是實(shí)現(xiàn)通信通道,發(fā)送的是完全取決于應(yīng)用程序,。
- Flask-Sockets 僅僅將WebSocket協(xié)議(通過使用gevent-websocket項(xiàng)目)進(jìn)行包裝,,因此它 只適用于原生支持WebSocket協(xié)議的瀏覽器,對(duì)于那些不支持WebSocket協(xié)議的較老的瀏覽器,無法使用
- Flask-SocketIO
- Flask-SocketIO不僅實(shí)現(xiàn)了WebSocket協(xié)議,,并且對(duì)于那些不支持WebSocket協(xié)議的舊版瀏覽器,,使用它也能夠?qū)崿F(xiàn)相同的效果。新版舊版的瀏覽器都能使用他 ,。
- Flask-SocketIO 實(shí)現(xiàn)了SocketIO Javascript庫公開的消息傳遞協(xié)議,。
- Flask-SocketIO 還為事件處理程序創(chuàng)建了一個(gè)類似flask的常規(guī)視圖函數(shù)的環(huán)境,包括創(chuàng)建應(yīng)用程序和請(qǐng)求上下文,。 然而,,在文檔中會(huì)介紹一些重要的例外情形。
3-1 Socket.IO
- 官方
3-2 python-socketio
python-socketio
3-2-0 安裝
- 服務(wù)端安裝:pip install python-socketio
- 客戶端安裝:pip install "python-socketio[client]"
安裝問題 :module 'importlib._bootstrap' has no attribute 'SourceFileLoader'
錯(cuò)誤分析:setuptools版本過久,,需要更新
解決方式:python -m ensurepip --upgrade
3-2-1 服務(wù)端基本總結(jié) ?
# create a Socket.IO server # wrap with a WSGI application app = socketio.WSGIApp(sio) # sid:每個(gè)客戶端連接的唯一標(biāo)識(shí)符,,一個(gè)客戶端發(fā)送的所有事件具有相同的sid值 @sio.on('my custom event') def another_event(sid, data): # connetc函數(shù) 在客戶端連接時(shí)自動(dòng)調(diào)用 可用于驗(yàn)證用戶身份等 # environ 為字典格式,包含請(qǐng)求信息,、http頭 def connect(sid, environ): # 若返回 False 則表示拒絕與客戶端的聯(lián)系 # 拋錯(cuò),,將所有參數(shù)通過拒絕消息發(fā)送給客戶端 raise ConnectionRefusedError('authentication failed') # disconnect函數(shù) 在客戶端斷開連接時(shí)自動(dòng)調(diào)用 print('disconnect ', sid) # socketio.Server.emit() 發(fā)送事件 # sio.emit('事件名',{'具體信息數(shù)據(jù)': '……'}) sio.emit('my event', {'data': 'foobar'}) # room 用于標(biāo)識(shí)應(yīng)接受該事件的客戶端sid,需要設(shè)置客戶端的sid,,若省略則表示廣播事件 sio.emit('my event', {'data': 'foobar'}, room='user_sid') # callback 回調(diào)函數(shù),,將在客戶端處理事件后調(diào)用該函數(shù),客戶端返回的任何值都將作為參數(shù)給予該回調(diào)函數(shù),。 # 若在廣播的情況下使用回調(diào),,則服務(wù)端將有大量的調(diào)用次數(shù) sio.emit('my event', {'data': 'foobar'}, callback=my_event) # client 為每個(gè)連接制定不同的命名空間,來打開多個(gè)連接,,命名空間將作為主機(jī)名和端口后的路徑名 @sio.event(namespace='/chat') def my_custom_event(sid, data): @sio.on('my custom event', namespace='/chat') def my_custom_event(sid, data): # socketio.Namespace 基于類的命名空間 # 注意:基于類的命名空間為單例,,所以命名空間實(shí)例不能用于存儲(chǔ)客戶端的特定消息 class MyCustomNamespace(socketio.Namespace): # 服務(wù)器接受的任何事件,都將調(diào)用 on_ 前綴的事件名方法 # 若接受到的事件名在類內(nèi)無匹配on前綴方法,,則忽略,。 def on_connect(self, sid, environ): def on_disconnect(self, sid): # my_event 事件觸發(fā) on_my_event 方法的執(zhí)行 def on_my_event(self, sid, data): self.emit('my_response', data) sio.register_namespace(MyCustomNamespace('/test')) # socketio.Server.enter_room() 和 socketio.Server.leave_room()方法管理其中的客戶端 sio.enter_room(sid, 'chat_users') sio.leave_room(sid, 'chat_users') # skip_sid 用于跳過該sid的客戶端,不進(jìn)行消息推送 def my_message(sid, data): sio.emit('my reply', data, room='chat_users', skip_sid=sid) # 注意:客戶端斷開連接時(shí),,會(huì)破壞用戶會(huì)話的內(nèi)容,。 # 特別是,當(dāng)客戶端在意外斷開與服務(wù)器的連接后重新連接時(shí),,不會(huì)保留用戶會(huì)話內(nèi)容,。 def connect(sid, environ): # username = authenticate_user(environ) sio.save_session(sid, {'username': username}) session = sio.get_session(sid) print('message from ', session['username']) def connect(sid, environ): # username = authenticate_user(environ) with sio.session(sid) as session: session['username'] = username with sio.session(sid) as session: print('message from ', session['username'])
3-2-3 一對(duì)多長連接
3-2 Flask-SocketIO
Flask-SocketIO - github
from flask import Flask, render_template from flask_socketio import SocketIO app.config['SECRET_KEY'] = 'secret!' print('connection established') print('received json: ' + str(json)) @socketio.on('disconnect') print('Client disconnected') if __name__ == '__main__':
四,、WebSocket for client - 單純用于連接的模塊
websocket-client github
安裝:Type "python setup.py install" or "pip install websocket-client" to install.
五,、Tornado - 一個(gè)支持HTTP和WebSocket的Web框架
Tornado 官方文檔
WebSocket - Tornado 的基本實(shí)現(xiàn)總結(jié)
六、Aiohttp - 基于asyncio,,一個(gè)支持HTTP和WebSocket的框架
aiohttp 官方文檔
|