欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Python使用WebSocket和SSE實現(xiàn)HTTP服務器消息推送方式

 更新時間:2024年11月22日 15:07:40   作者:slp_44777680  
本文介紹了兩種實時數(shù)據(jù)獲取的技術:WebSocket和SSE,WebSocket是全雙工通信協(xié)議,支持雙向通信,但需要專門定義數(shù)據(jù)協(xié)議,SSE是一種單工通信技術,基于HTTP的流式數(shù)據(jù)傳輸,客戶端開發(fā)簡單,但只能單工通信

很多時候我們需要實時獲取最新數(shù)據(jù),但是傳統(tǒng)意義上的HTTP請求,必須由客戶端向服務端發(fā)起請求,服務端再返回相應的數(shù)據(jù)。

那如果我們需要獲取實時數(shù)據(jù),就要通過HTTP輪詢,客戶端不間斷的向服務器發(fā)起請求。

這樣不斷的的請求不但嚴重加大服務器的壓力,還可能因為網(wǎng)絡延遲而影響數(shù)據(jù)的時效性。

下面介紹兩種方法能夠很好的滿足業(yè)務的需求。

一、WebSocket

WebSocket是HTML5開始提供的一種在單個 TCP 鏈接上進行全雙工通信的協(xié)議。

  • 優(yōu)點:雙工通信
  • 缺點:需專門定義數(shù)據(jù)協(xié)議,解析數(shù)據(jù)流,且部分服務器支持不完善,后臺例如java spring boot 2.1.2 僅支持websocket 1.0(最高已達1.3)

1.客戶端代碼

python 3+ 代碼

#python 3+
import threading
import websocket


class Client:
    def __init__(self,url):
    	self.url = url
        self.ws = None
        self.enable = True


    def on_message(self,response):
		self.enable = False
        print(response)


    def on_error(self,error):
        # print(ws)
        print(error)


    def on_close(self):
    	self.enable = True
        print(ws)
       

    def on_open(self):
		print('open')
    
    def start_func(self):

        while self.enable:
            websocket.enableTrace(True)
            self.ws = websocket.WebSocketApp(self.url,
                                        on_message=self.on_message,
                                        # on_data=on_data,
                                        on_error=self.on_error,
                                        on_open=self.on_open,
                                        on_close=self.on_close, )


            self.ws.run_forever(ping_interval=60, ping_timeout=5)

if __name__ == "__main__":
	cli = Client(url = 'wss://api.zb.live/websocket' )
    t1 = threading.Thread(target=cli.start_func_zb)
    t1.start()

javascript 代碼

var ws = new WebSocket("wss://echo.websocket.org");

ws.onopen = function(evt) { 
  console.log("Connection open ..."); 
  ws.send("Hello WebSockets!");
};

ws.onmessage = function(evt) {
  console.log( "Received Message: " + evt.data);
  ws.close();
};

ws.onclose = function(evt) {
  console.log("Connection closed.");
};      

2.服務端代碼

from websocket_server import WebsocketServer

class WSSocketObj:
    def __init__(self,host=None,port = 8131):
       self.host = host if host else '127.0.0.1'
       self.port = port

    # 當新的客戶端連接時會提示
    def new_client(self,client, server,):
        print("當新的客戶端連接時會提示:%s" % client['id'])
        dd = 122
        server.send_message_to_all("Hey all, a new client has joined us")

    # 當舊的客戶端離開
    def client_left(self,client, server):
        print("客戶端%s斷開" % client['id'])

    # 接收客戶端的信息。
    def message_received(self,client, server, message):
        print("Client(%d) said: %s" % (client['id'], message))
        # server.send_message_to_all(message) #發(fā)送消息給 全部客戶端
        server.send_message(client, 'hello,client')  # 發(fā)送消息給指定客戶端

    def run_server(self):
        server = WebsocketServer(self.port, self.host)
        server.set_fn_new_client(self.new_client)
        server.set_fn_client_left(self.client_left)
        server.set_fn_message_received(self.message_received)
        server.run_forever()

if __name__ == '__main__':

    WSSocketObj().run_server()

二、SSE(Server-Sent Events,服務器發(fā)送事件)

SSE ( Server-sent Events )通俗解釋起來就是一種基于HTTP的,以流的形式由服務端持續(xù)向客戶端發(fā)送數(shù)據(jù)的技術,是 WebSocket 的一種輕量代替方案。

  • 優(yōu)點:開發(fā)簡單,和傳統(tǒng)的http開發(fā)幾乎無任何差別,客戶端開發(fā)簡單,有標準支持(EventSource)
  • 缺點:和websocket相比,只能單工通信,建立連接后,只能由服務端發(fā)往客戶端,且占用一個連接,如需客戶端向服務端通信,需額外打開一個連接

1.客戶端代碼

python

# 第一種方式
def sse_client():
    """
    pip install sseclient-py
    只對于返回標準SSE格式的請求可用 格式:event: {EVENT}\nretry: 10000\ndata: {DATA}\n\n
    :return:
    """
    import requests
    # res = requests.request('get', url, json=data, stream=True, headers={'Accept': 'text/event-stream'})
    client = requests.post(url, json=data, stream=True, headers={'Accept': 'text/event-stream'})
    client = sseclient.SSEClient(client)
    for i in client.events():
        print(i.data)

# 第二種方式
def sse_with_requests():
    headers = {"Accept": "text/event-stream"}
    r = requests.post(url, headers=headers, json=data, stream=True)
    r.encoding = 'utf-8'
    for chunk in r.iter_content(chunk_size=None, decode_unicode=True):
        # 處理接收到的數(shù)據(jù)塊
        print("Received:", chunk)

javascript

第一種方式:

//判斷是否支持SSE
if('EventSource' in window){

//初始化SSE
var url="http:localhost:8000/stream";
var source=new EventSource(url);

// 連接成功后會觸發(fā) open 事件
source.onopen=(event)=>{

console.log("開啟SSE");

}

// 服務器發(fā)送信息到客戶端時,如果沒有 event 字段,默認會觸發(fā) message 事件
source.onmessage=(event)=>{

var data=event.data;

$("body").append($("<p>").text(data));

}

//監(jiān)聽like事件
source.addEventListener('like',function(event){

var data=event.data;

$("body").append($("<p>").text(data));
},false);

// 連接異常時會觸發(fā) error 事件并自動重連
source.onerror=(event)=>{

console.log(event);

}

第二種方式:使用 addEventListener 方法來添加相應的事件處理方法

if (window.EventSource) {
  // 創(chuàng)建 EventSource 對象連接服務器
  const source = new EventSource('http://localhost:2000');

  // 連接成功后會觸發(fā) open 事件
  source.addEventListener('open', () => {
    console.log('Connected');
  }, false);

  // 服務器發(fā)送信息到客戶端時,如果沒有 event 字段,默認會觸發(fā) message 事件
  source.addEventListener('message', e => {
    console.log(`data: ${e.data}`);
  }, false);

  // 自定義 EventHandler,在收到 event 字段為 slide 的消息時觸發(fā)
  source.addEventListener('slide', e => {
    console.log(`data: ${e.data}`); // => data: 7
  }, false);

  // 連接異常時會觸發(fā) error 事件并自動重連
  source.addEventListener('error', e => {
    if (e.target.readyState === EventSource.CLOSED) {
      console.log('Disconnected');
    } else if (e.target.readyState === EventSource.CONNECTING) {
      console.log('Connecting...');
    }
  }, false);
} else {
  console.error('Your browser doesn\'t support SSE');
}

EventSource從父接口 EventTarget 中繼承了屬性和方法,其內置了 3 個 EventHandler 屬性、2 個只讀屬性和 1 個方法:

EventHandler 屬性

  • EventSource.onopen 在連接打開時被調用。
  • EventSource.onmessage 在收到一個沒有 event 屬性的消息時被調用。
  • EventSource.onerror 在連接異常時被調用。 只讀屬性
  • EventSource.readyState 一個 unsigned short 值,代表連接狀態(tài)??赡苤凳?CONNECTING (0), OPEN (1), 或者 CLOSED (2)。
  • EventSource.url連接的 URL。 方法
  • EventSource.close() 關閉連接

EventSource 對象的 onmessage 屬性的作用類似于 addEventListener( ‘ message ’ )

2.服務端代碼(基于Flask)

import json
import time

from flask import Flask, request
from flask import Response
from flask import render_template

app = Flask(__name__)


def get_message():
    """this could be any function that blocks until data is ready"""
    time.sleep(1)
    s = time.ctime(time.time())
    return json.dumps(['當前時間:' + s , 'a'], ensure_ascii=False)


@app.route('/')
def hello_world():
    return render_template('index.html')


@app.route('/stream')
def stream():
    user_id = request.args.get('user_id')
    print(user_id)
    def eventStream():
        id = 0
        while True:
            id +=1
            # wait for source data to be available, then push it

            yield 'id: {}\nevent: add\ndata: {}\n\n'.format(id,get_message())


    return Response(eventStream(), mimetype="text/event-stream")


if __name__ == '__main__':
    app.run()

因為SSE是http請求,可是又限定是一個長鏈接,因此要設置MIME類型為text/event-stream。返回的為字符串。 

消息的格式

服務器向瀏覽器發(fā)送的 SSE 數(shù)據(jù),必須是 UTF-8 編碼的文本;

每一次發(fā)送的信息,由若干個message組成,每一個message之間用\n\n分隔。每一個message內部由若干行組成

  • 格式
[field]:value\n

其中在規(guī)范中為消息定義了 4 個字段

  • id 表明id
  • event 表明消息的事件類型
  • data 消息的數(shù)據(jù)字段
  • retry 客戶端重連的時間。只接受整數(shù),單位是毫秒。如果這個值不是整數(shù)則會被自動忽略

需要注意的是,id字段不是必須的,服務器有可能不會在消息中帶上 id 字段,這樣子客戶端就不會存在 Last-Event-ID這個屬性。所以為了保證數(shù)據(jù)可靠,我們需要在每條消息上帶上 id 字段。

一個很有意思的地方是,規(guī)范中規(guī)定以冒號開頭的消息都會被當作注釋,一條普通的注釋(:\n\n)對于服務器來說只占 5個字符,但是發(fā)送到客戶端上的時候不會觸發(fā)任何事件,這對客戶端來說是非常友好的。所以注釋一般被用于維持服務器和客戶端的長連接。

3.SSE使用注意事項

1、SSE 如何保證數(shù)據(jù)完整性

客戶端在每次接收到消息時,會把消息的 id 字段作為內部屬性 Last-Event-ID 儲存起來。

SSE 默認支持斷線重連機制,在連接斷開時會 觸發(fā) EventSource 的 error 事件,同時自動重連。再次連接成功時EventSource 會把 Last-Event-ID 屬性作為請求頭發(fā)送給服務器,這樣服務器就可以根據(jù)這個 Last-Event-ID作出相應的處理。

這里需要注意的是,id 字段不是必須的,服務器有可能不會在消息中帶上 id 字段,這樣子客戶端就不會存在 Last-Event-ID這個屬性。所以為了保證數(shù)據(jù)可靠,我們需要在每條消息上帶上 id 字段。

2、減少開銷

在 SSE 的草案中提到,“text/event-stream” 的 MIME 類型傳輸應當在靜置 15秒后自動斷開。在實際的項目中也會有這個機制,但是斷開的時間沒有被列入標準中。

為了減少服務器的開銷,我們也可以有目的的斷開和重連。

簡單的辦法是服務器發(fā)送一個 關閉消息并指定一個重連的時間戳,客戶端在觸發(fā)關閉事件時關閉當前連接并創(chuàng)建 一個計時器,在重連時把計時器銷毀。

function connectSSE() {
  if (window.EventSource) {
    const source = new EventSource('http://localhost:2000');
    let reconnectTimeout;

    source.addEventListener('open', () => {
      console.log('Connected');
      clearTimeout(reconnectTimeout);
    }, false);

    source.addEventListener('pause', e => {
      source.close();
      const reconnectTime = +e.data;
      const currentTime = +new Date();
      reconnectTimeout = setTimeout(() => {
        connectSSE();
      }, reconnectTime - currentTime);
    }, false);
  } else {
    console.error('Your browser doesn\'t support SSE');
  }
}

connectSSE();

總結

以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。

相關文章

  • Python3數(shù)據(jù)庫操作包pymysql的操作方法

    Python3數(shù)據(jù)庫操作包pymysql的操作方法

    這篇文章主要介紹了Python3數(shù)據(jù)庫操作包pymysql的操作方法,文章通過實例代碼相結合給大家介紹的非常詳細,需要的朋友可以參考下
    2018-07-07
  • Python使用os.listdir()和os.walk()獲取文件路徑與文件下所有目錄的方法

    Python使用os.listdir()和os.walk()獲取文件路徑與文件下所有目錄的方法

    今天小編就為大家分享一篇關于Python使用os.listdir()和os.walk()獲取文件路徑與文件下所有目錄的方法,小編覺得內容挺不錯的,現(xiàn)在分享給大家,具有很好的參考價值,需要的朋友一起跟隨小編來看看吧
    2019-04-04
  • python字典鍵值對的添加和遍歷方法

    python字典鍵值對的添加和遍歷方法

    下面小編就為大家?guī)硪黄猵ython字典鍵值對的添加和遍歷方法。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2016-09-09
  • Python操作word文檔的示例詳解

    Python操作word文檔的示例詳解

    本文為大家介紹了Python操作docx文檔相關知識點。主要涉及的內容為python-docx?,一款可以操作Word文檔(僅支持docx)的第三方庫??旄S小編一起學習一下吧
    2022-01-01
  • python學習之讀取配置文件

    python學習之讀取配置文件

    這篇文章主要介紹了python學習之讀取配置文件,文章基于python的相關資料展開對主題的詳細介紹,具有一定的參考價值,需要的小伙伴可以參考一下
    2022-04-04
  • Pandas中DataFrame對象轉置(交換行列)

    Pandas中DataFrame對象轉置(交換行列)

    本文主要介紹了Pandas中DataFrame對象轉置(交換行列),文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2023-02-02
  • Python程序中設置HTTP代理

    Python程序中設置HTTP代理

    本文主要給大家簡單講解了下http代理的概念以及如何在Python程序中設置http代理的方法,非常的詳細,有需要的小伙伴可以參考下
    2016-11-11
  • python字符串替換示例

    python字符串替換示例

    這篇文章主要介紹了python字符串替換示例,需要的朋友可以參考下
    2014-04-04
  • windows下pycharm搭建spark環(huán)境并成功運行 附源碼

    windows下pycharm搭建spark環(huán)境并成功運行 附源碼

    這篇文章主要介紹了windows下pycharm搭建spark環(huán)境并成功運行 附源碼,本文分步驟給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2021-04-04
  • 使用python BeautifulSoup庫抓取58手機維修信息

    使用python BeautifulSoup庫抓取58手機維修信息

    這篇文章主要介紹了一個使用python抓取58手機的精準商家信息,使用BeautifulSoup API的方法
    2013-11-11

最新評論