SoFunction
Updated on 2025-03-04

Python uses WebSocket and SSE to implement HTTP server message push method

Many times we need to get the latest data in real time, but in the traditional sense, HTTP requests must be initiated by the client to the server, and the server then returns the corresponding data.

If we need to obtain real-time data, we have to poll it through HTTP and the client continuously initiates requests to the server.

Such continuous requests not only seriously increase the pressure on the server, but may also affect the timeliness of data due to network delay.

The following are two methods that can meet business needs well.

1. WebSocket

WebSocket is a protocol that HTML5 began to provide for full duplex communication on a single TCP link.

  • advantage: Duplex communication
  • shortcoming: It is necessary to specifically define the data protocol to parse the data stream, and some servers support is incomplete. For example, Java spring boot 2.1.2 only supports websocket 1.0 (up to 1.3)

1. Client code

python 3+ code

#python 3+
import threading
import websocket


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


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


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


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

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

        while :
            (True)
             = (,
                                        on_message=self.on_message,
                                        # on_data=on_data,
                                        on_error=self.on_error,
                                        on_open=self.on_open,
                                        on_close=self.on_close, )


            .run_forever(ping_interval=60, ping_timeout=5)

if __name__ == "__main__":
	cli = Client(url = 'wss:///websocket' )
    t1 = (target=cli.start_func_zb)
    ()

javascript code

var ws = new WebSocket("wss://");

 = function(evt) { 
  ("Connection open ..."); 
  ("Hello WebSockets!");
};

 = function(evt) {
  ( "Received Message: " + );
  ();
};

 = function(evt) {
  ("Connection closed.");
};      

2. Server code

from websocket_server import WebsocketServer

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

    # When a new client connects, it will prompt    def new_client(self,client, server,):
        print("When a new client connects, it will prompt: %s" % client['id'])
        dd = 122
        server.send_message_to_all("Hey all, a new client has joined us")

    # When the old client leaves    def client_left(self,client, server):
        print("Client %s disconnected" % client['id'])

    # Receive client information.    def message_received(self,client, server, message):
        print("Client(%d) said: %s" % (client['id'], message))
        # server.send_message_to_all(message) #Send messages to all clients        server.send_message(client, 'hello,client')  # Send a message to the specified client
    def run_server(self):
        server = WebsocketServer(, )
        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()

2. SSE (Server-Sent Events, server sends events)

SSE(Server-sent Events) In a common sense, it is a technology based on HTTP and continuously sending data from the server to the client in the form of a stream. It is a lightweight alternative to WebSocket.

  • advantage: Simple development, almost no difference from traditional http development, simple client development, standard support (EventSource)
  • shortcoming: Compared with websocket, it can only communicate with simplex. After establishing a connection, it can only be sent to the client from the server and occupy one connection. If the client needs to communicate with the server, an additional connection needs to be opened.

1. Client code

python

#The first methoddef sse_client():
    """
    pip install sseclient-py
    For return criteria onlySSEFormat requests available Format:event: {EVENT}\nretry: 10000\ndata: {DATA}\n\n
    :return:
    """
    import requests
    # res = ('get', url, json=data, stream=True, headers={'Accept': 'text/event-stream'})
    client = (url, json=data, stream=True, headers={'Accept': 'text/event-stream'})
    client = (client)
    for i in ():
        print()

# The second waydef sse_with_requests():
    headers = {"Accept": "text/event-stream"}
    r = (url, headers=headers, json=data, stream=True)
     = 'utf-8'
    for chunk in r.iter_content(chunk_size=None, decode_unicode=True):
        # Process the received data blocks        print("Received:", chunk)

javascript

The first method:

//Judge whether SSE is supportedif('EventSource' in window){

//Initialize SSEvar url="http:localhost:8000/stream";
var source=new EventSource(url);

// Open event will be triggered after the connection is successful=(event)=>{

("Open SSE");

}

// If there is no event field when the server sends information to the client, the message event will be triggered by default.=(event)=>{

var data=;

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

}

// Listen to like events('like',function(event){

var data=;

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

// An error event will be triggered when the connection is abnormal and it will automatically reconnect.=(event)=>{

(event);

}

The second method: use the addEventListener method to add the corresponding event handling method

if () {
  // Create EventSource object to connect to the server  const source = new EventSource('http://localhost:2000');

  // Open event will be triggered after the connection is successful  ('open', () => {
    ('Connected');
  }, false);

  // If there is no event field when the server sends information to the client, the message event will be triggered by default.  ('message', e => {
    (`data: ${}`);
  }, false);

  // Custom EventHandler, triggered when receiving a message with the event field slide  ('slide', e => {
    (`data: ${}`); // => data: 7
  }, false);

  // An error event will be triggered when the connection is abnormal and it will automatically reconnect.  ('error', e => {
    if ( === ) {
      ('Disconnected');
    } else if ( === ) {
      ('Connecting...');
    }
  }, false);
} else {
  ('Your browser doesn\'t support SSE');
}

EventSourceFrom the parent interfaceEventTargetInherited attributes and methods, it has 3 built-inEventHandler Properties, 2 read-only properties, and 1 method:

EventHandlerproperty

  • Called when the connection is opened.
  • Called when a message without an event attribute is received.
  • Called during connection exception. Read-only properties
  • An unsigned short value representing the connection status. Possible values ​​are CONNECTING (0), OPEN (1), or CLOSED (2).
  • The URL to the connection. method
  • ()Close the connection

EventSource object'sonmessageThe function of attributes is similar toaddEventListener( ‘ message ’ )

2. Server-side code (based on 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"""
    (1)
    s = (())
    return (['Current time:' + s , 'a'], ensure_ascii=False)


@('/')
def hello_world():
    return render_template('')


@('/stream')
def stream():
    user_id = ('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__':
    ()

Because SSE is an http request, but it is limited to a long link, the MIME type must be set to text/event-stream. The returned string.

Message format

The SSE data sent by the server to the browser must be UTF-8 encoded text;

Each message is sent consists of several messages, and each message is separated by \n\n. Each message consists of several rows

  • Format
[field]:value\n

Among them, 4 fields are defined for messages in the specification

  • id indicates id
  • event Indicates the event type of the message
  • Data field of data message
  • retry The time for the reconnection of the client. Only integers are accepted, in milliseconds. If this value is not an integer, it will be automatically ignored

It should be noted that the id field is not necessary, and the server may not include the id field in the message, so that the sub-client will not have the Last-Event-ID property. So in order to ensure the data is reliable, we need to bring the id field on each message.

A very interesting thing is that the specification stipulates that messages starting with a colon will be regarded as comments, a normal comment (:\n\n) It only takes up 5 characters for the server, but no events will be triggered when sent to the client, which is very friendly to the client. Therefore, comments are generally used to maintain long connections between servers and clients.

Notes on using

1. How to ensure data integrity in SSE

Every time the client receives a message, it will store the message id field as the internal property Last-Event-ID.

SSE supports disconnection and reconnection mechanism by default. When the connection is disconnected, the error event of EventSource will be triggered, and the reconnection will be automatically reconnected. When the connection is successful again, EventSource will send the Last-Event-ID property to the server as a request header, so that the server can perform corresponding processing based on this Last-Event-ID.

It should be noted here that the id field is not necessary, and the server may not include the id field in the message, so that the sub-client will not have the Last-Event-ID property. So in order to ensure the data is reliable, we need to bring the id field on each message.

2. Reduce overhead

In the draft SSE, it is mentioned that the MIME type transmission of "text/event-stream" should be automatically disconnected after being left to stand for 15 seconds. This mechanism will also exist in actual projects, but the disconnection time is not included in the standard.

In order to reduce server overhead, we can also disconnect and reconnect for purpose.

The simple way is to send a close message and specify a reconnection timestamp. The client closes the current connection when the closing event is triggered and creates a timer, and destroys the timer when the reconnection is reconnected.

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

    ('open', () => {
      ('Connected');
      clearTimeout(reconnectTimeout);
    }, false);

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

connectSSE();

Summarize

The above is personal experience. I hope you can give you a reference and I hope you can support me more.