WebSocket協(xié)議和Socket.IO框架

一、WebSocket介紹:

1,WebSocket是什么?

WebSocket是一種在單個(gè)TCP連接上進(jìn)行全雙工通信的協(xié)議。在WebSocket API中,瀏覽器和服務(wù)器只需要完成一次握手(不是指建立TCP連接的那個(gè)三次握手,是指在建立TCP連接后傳輸一次握手?jǐn)?shù)據(jù)),兩者之間就直接可以創(chuàng)建持久性的連接,并進(jìn)行雙向數(shù)據(jù)傳輸。

WebSocket是H5新增的一重通信協(xié)議,為了更好的節(jié)省服務(wù)器資源和帶寬,并且能夠更實(shí)時(shí)地進(jìn)行通訊。

2,WebSocket與TCP、HTTP之間有什么關(guān)系?

WebSocket和HTTP都是基于TCP創(chuàng)建的,所以發(fā)送消息之前都要?jiǎng)?chuàng)建連接進(jìn)行三次握手。

WebSocket依賴HTTP協(xié)議的一次握手,一次握手成功之后,就不需要HTTP了,而是使用TCP的傳輸通道,開始收發(fā)數(shù)據(jù),這就是上面所說的雙向連接。

3,WebSocket使用場景?

  • 用戶下了訂單,運(yùn)營管理后臺需要向運(yùn)營人員推送訂單通知。
  • 用戶A關(guān)注了用戶B,需要向用戶B推送消息
  • 有一些游戲,既支持手機(jī)端,也支持web端時(shí),需要用到。
  • 網(wǎng)頁版即時(shí)聊天、彈幕等

4,為什么選擇WebSocket而不是其他?

  1. HTTP/1.x 版本不支持服務(wù)器主動推送,只能在客戶端發(fā)起請求后做出回應(yīng)。
  2. HTTP/2.x版本支持服務(wù)器主動推送,但HTTP/2 還未全面實(shí)施
  3. 輪詢 :每隔一段時(shí)間就會想服務(wù)器發(fā)送請求,詢問是否有新的消息,必須不停連接,或者連接始終打開,效率低下,浪費(fèi)資源。
  4. Comet (基于長連接)長輪詢是在打開一條連接以后保持,等待服務(wù)器推送來數(shù)據(jù)再關(guān)閉的方式。依然需要反復(fù)發(fā)出請求,而且長連接也會消耗服務(wù)器資源。

綜上所述:HTTP、輪詢、長輪詢都不太合適。而WebSocket呢?看下面介紹:

5,典型的Websocket握手請求如下:

客戶端請求:

GET / HTTP/1.1
Upgrade: websocket    
#Upgrade字段必須設(shè)置Websocket,表示希望升級到Websocket協(xié)議

Connection: Upgrade   
#Connection必須設(shè)置Upgrade,表示客戶端希望連接升級

Host: example.com
Origin: http://example.com
Sec-WebSocket-Key: N9cRrP/n9NdMgdcy2VJFQghu== 
#Sec-WebSocket-Key是一串隨機(jī)的字符串,服務(wù)器使用SHA算法,base64編碼之后,將結(jié)果做為“Sec-WebSocket-Accept”頭的值,返回給客戶端。如此操作,可以**盡量避免普通HTTP請求被誤認(rèn)為Websocket協(xié)議**

Sec-WebSocket-Version: 13

服務(wù)器回應(yīng):

HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: BooB7FAkLlXgRSz0BT3v4hq5s=
Sec-WebSocket-Location: ws://example.com/

握手過程解析:

第一步:服務(wù)端開啟socket,監(jiān)聽某個(gè)ip和端口。等待連接...
第二步:客戶端connect連接服務(wù)端(ip,端口)
第三步:服務(wù)端允許連接...
第四部:客戶端發(fā)送握手請求,客戶端自己生成的一個(gè)特殊值發(fā)給服務(wù)器(魔法字符串migic string),發(fā)送之前客戶端會自己先加密,保存。
第五步:服務(wù)器接收到特殊字符串后,經(jīng)過SHA1加密,加密后把值發(fā)給客戶端
第六步:客戶端接收到加密后的值后,與之前的保存的值校驗(yàn),如果校驗(yàn)通過,說明對方也是websocket協(xié)議,ok,握手成功,可以雙方收發(fā)數(shù)據(jù)了。

Websocket總結(jié):

  • 節(jié)省資源開銷。服務(wù)器到客戶端的內(nèi)容,頭部大小只有2至10字節(jié)(和數(shù)據(jù)包長度有關(guān)),而HTTP請求每次都要攜帶完整的頭部。
  • 更強(qiáng)的實(shí)時(shí)性。因?yàn)閰f(xié)議是全雙工的,所以服務(wù)器可以隨時(shí)主動給客戶端下發(fā)數(shù)據(jù)。
  • 保持連接狀態(tài)。Websocket需要先創(chuàng)建連接,這就使得其成為一種有狀態(tài)的協(xié)議,之后通信時(shí)可以省略部分狀態(tài)信息。而HTTP請求可能需要在每個(gè)請求都攜帶狀態(tài)信息(如身份認(rèn)證等)。
  • 更好的二進(jìn)制支持。Websocket定義了二進(jìn)制幀,相對HTTP,可以更輕松地處理二進(jìn)制內(nèi)容。
  • 可擴(kuò)展。Websocket定義了擴(kuò)展,用戶可以擴(kuò)展協(xié)議、實(shí)現(xiàn)部分自定義的子協(xié)議。如部分瀏覽器支持壓縮等。
  • 更好的壓縮效果。相對于HTTP壓縮,Websocket在適當(dāng)?shù)臄U(kuò)展支持下,可以沿用之前內(nèi)容的上下文,在傳遞類似的數(shù)據(jù)時(shí),可以顯著地提高壓縮率。
  • 沒有同源限制??蛻舳丝梢耘c任意服務(wù)器通信。
  • 可以發(fā)送文本,也可以發(fā)送二進(jìn)制數(shù)據(jù)

二、Socket.IO介紹:

1,Socket.IO是什么?

Socket.IO 基于WebSocket協(xié)議,現(xiàn)在已成為Web即時(shí)通訊應(yīng)用的框架,WebSocket只是Socket.IO實(shí)現(xiàn)即時(shí)通訊的其中一種技術(shù)依賴。

2,Socket.IO優(yōu)點(diǎn):

Socket.IO 會自動選擇合適雙向通信協(xié)議
有Python庫的實(shí)現(xiàn),可以在Python實(shí)現(xiàn)的Web應(yīng)用中去實(shí)現(xiàn)IM后臺服務(wù)。
https://python-socketio.readthedocs.io/en/latest/server.html

3,Socket.IO缺點(diǎn):

Socket.io要求客戶端與服務(wù)器端均須使用該框架。

三、python 服務(wù)器端開發(fā)

1,安裝:

pip install python-socketio

2,服務(wù)器部署方案:

下面主要使用協(xié)程eventlet庫部署服務(wù)器,其他服務(wù)器部署方案,后面會詳細(xì)介紹。

選擇協(xié)程方案的原因:

  • 使用eventlet部署的Socket.IO服務(wù)器可以訪問長輪詢和WebSocket傳輸。
  • 也可以使用協(xié)程gevent部署,只不過gevent不支持websocket,要安裝gevent-websocket 包才行。
  • 若服務(wù)器采用多進(jìn)程或多線程方式,受限于服務(wù)器能創(chuàng)建的進(jìn)程或線程數(shù),能夠支持的并發(fā)連接客戶端不會很高,服務(wù)器性能有限。
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)

3,事件處理方法如下:

@sio.on('connect')
def on_connect(sid, environ):
    """
    與客戶端建立好連接后被執(zhí)行
    :param sid: string sid是socketio為當(dāng)前連接客戶端生成的識別id
    :param environ: dict 在連接握手時(shí)客戶端發(fā)送的握手?jǐn)?shù)據(jù)(HTTP報(bào)文解析之后的字典)
    """
    pass

@sio.on('disconnect')
def on_disconnect(sid):
    """
    與客戶端斷開連接后被執(zhí)行
    :param sid: string sid是斷開連接的客戶端id
    """
    pass

# 以字符串的形式表示一個(gè)自定義事件,事件的定義由前后端約定
@sio.on('my custom event')  
def my_custom_event(sid, data):
    """
    自定義事件消息的處理方法
    :param sid: string sid是發(fā)送此事件消息的客戶端id
    :param data: data是客戶端發(fā)送的消息數(shù)據(jù)
    """
    pass

提示:

SocketIO:對收發(fā)的數(shù)據(jù)以消息(message)來對待,收發(fā)的不同類別的消息數(shù)據(jù)又以事件(event)來區(qū)分。
connect 為特殊事件,當(dāng)客戶端連接后自動執(zhí)行
disconnect 為特殊事件,當(dāng)客戶端斷開連接后自動執(zhí)行
connect、disconnect與自定義事件處理方法的函數(shù)傳入?yún)?shù)不同

4,發(fā)送消息(emit)

發(fā)送消息可以單獨(dú)發(fā)給某個(gè)用戶,也可以群發(fā),SocketIO提供了房間(room)來為客戶端分組,也可以發(fā)到某個(gè)房間,參考如下方法:

1.發(fā)給某個(gè)用戶(指定用戶id)

sio.emit('my event', {'data': 'foobar'}, room=user_sid)

2.群發(fā)(不指定id號)

sio.emit('my event', {'data': 'foobar'})

3.將連接的客戶端放入一個(gè)room中 : sio.enter_room(sid, room_name)

 @sio.on('chat')
  def begin_chat(sid):
      sio.enter_room(sid, 'chat_users')

4.將客戶從某個(gè)房間移除: sio.leave_room(sid, room_name)

@sio.on('exit_chat')
  def exit_chat(sid):
      sio.leave_room(sid, 'chat_users')

5.查詢sid所在的房間,給一組用戶發(fā)送消息:sio.rooms(sid)

@sio.on('my message')
def message(sid, data):
  sio.emit('my reply', data, room='chat_users')

6.也可跳過某個(gè)客戶端,使用 skip_sid參數(shù)

@sio.event
def message(sid, data):
    sio.emit('my reply', data, room='chat_users', skip_sid=sid)

7.使用send發(fā)送message事件消息

sio.send({'data': 'foobar'})
sio.send({'data': 'foobar'}, room=user_sid)

機(jī)器人聊天服務(wù)實(shí)現(xiàn)

第一步:創(chuàng)建AI文件夾,創(chuàng)建server.py

import socketio

# 創(chuàng)建sio對象
sio = socketio.Server(async_mode='eventlet')
app = socketio.Middleware(sio)

第二步:創(chuàng)建chart.py聊天室

import time
from server import sio

@sio.on("connect")
def on_connect(sid, environ):
    """
    與客戶端建立好連接后被執(zhí)行

    :param sid: string sid是socketio為當(dāng)前連接客戶端生成的識別id
    :param environ: dict 在連接握手時(shí)客戶端發(fā)送的握手?jǐn)?shù)據(jù)(HTTP報(bào)文解析之后的字典)
    
    # sio.emit(消息事件類型, 消息數(shù)據(jù)內(nèi)容, 接收人)
    """
 
    data = {
        "msg": "hello carry",   #發(fā)送的內(nèi)容
        'timestamp': round(time.time() * 1000)   #時(shí)間戳
    }

    sio.emit("message", data, room=sid)  


@sio.on("message")
def on_message(sid, data):
    """
    客戶端發(fā)送消息,on_message執(zhí)行

    :param sid: string sid是socketio為當(dāng)前連接客戶端生成的識別id
    :param environ: dict 在連接握手時(shí)客戶端發(fā)送的握手?jǐn)?shù)據(jù)(HTTP報(bào)文解析之后的字典)

    # sio.emit(消息事件類型, 消息數(shù)據(jù)內(nèi)容, 接收人)
    """

    resp_data = {
        "msg": "hello carry~~~,接收到你的消息:{}".format(data.get('msg')),
        'timestamp': round(time.time() * 1000)
    }

    sio.emit("message", resp_data, room=sid)

運(yùn)行main.py

import eventlet
eventlet.monkey_patch()   
#使用猴子補(bǔ)丁,在遇到阻塞時(shí),不用修改源代碼,也能執(zhí)行下面代碼

from server import app
import chat

# 創(chuàng)建eventlet服務(wù)器對象
sock = eventlet.listen(('0.0.0.0', 8000)) 

# 啟動socketio服務(wù)器
eventlet.wsgi.server(sock, app)

溫馨提示:
1.測試之前需要在谷歌上安裝Firecamp插件


Firecamp.png

2.啟動終端命令:

python main.py

3.點(diǎn)擊網(wǎng)址后面小火苗(Firecamp)標(biāo)志,進(jìn)入上圖,點(diǎn)擊Socket.IO進(jìn)入以下圖片,流程及步驟。


屏幕快照 2019-08-26 下午4.34.08.png

參考資料:

AIOHTTP:https://aiohttp.readthedocs.io/en/stable/
python-socket:https://python-socketio.readthedocs.io/en/latest/server.html
flask-socket:https://flask-socketio.readthedocs.io/en/latest/

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • WebSocket簡介 談到Web實(shí)時(shí)推送,就不得不說WebSocket。在WebSocket出現(xiàn)之前,很多網(wǎng)站為...
    吧啦啦小湯圓閱讀 8,314評論 15 75
  • 引用:https://www.cnblogs.com/mazg/p/5467960.html 一 websocke...
    馮艷輝brook閱讀 1,900評論 0 51
  • Socket.io提供了基于事件的實(shí)時(shí)雙向通訊 Browser和WebServer間的實(shí)時(shí)數(shù)據(jù)傳輸是一個(gè)很重要的需...
    JunChow520閱讀 94,255評論 14 97
  • WebSocket 機(jī)制 WebSocket 是 HTML5 一種新的協(xié)議。它實(shí)現(xiàn)了瀏覽器與服務(wù)器全雙工通信,能更...
    勇敢的_心_閱讀 2,373評論 0 4
  • 使用Windows的同學(xué)都知道,我們可以利用各種安全衛(wèi)士以及系統(tǒng)清理工具來清除系統(tǒng)運(yùn)行時(shí)產(chǎn)生的系統(tǒng)垃圾。那么當(dāng)你禁...
    鐵匠簡記閱讀 26,564評論 0 1

友情鏈接更多精彩內(nèi)容