简体   繁体   中英

Python-socketio with eventlet uses polling instead of websockets

Problem

Using eventlet 0.25.0 and python-socketio 4.0.3 w/ Python 3.7, I was attempting to have two Python scripts communicate with websockets. The server script worked fine:

import socketio   # type: ignore
import eventlet
import json
import util as u  # type: ignore

eventlet.monkey_patch()

WSGI_LOG_FORMAT = '%(client_ip)s "%(request_line)s" %(status_code)s %(body_length)s %(wall_seconds).6f'


class DatabaseServer:
    """ Initializes a server using socketio and eventlet. Use start() and stop() to... start and stop it.
        push() pushes new data to all clients
    """
    def __init__(self):
        self.server = socketio.Server(async_mode='eventlet', namespace='/socket.io')
        self.server.on('connect', self.connect, namespace='/socket.io')
        self.server.on('disconnect', self.disconnect, namespace='/socket.io')
        self.server.on('client_response', self.client_response, namespace='/socket.io')
        self.app = socketio.WSGIApp(self.server)
        self.realtime_timestamp = 0
        self.data_full = None
        self.data_update = None

    def connect(self, sid, environ):
        u.server_logger.info('Client connected: %s', sid)

    def client_response(self, sid, data):
        u.server_logger.info('Client %s sent: %s', sid, data)

    def disconnect(self, sid):
        u.server_logger.info('Client disconnected: %s', sid)

    def push(self):
        u.server_logger.info('Pushing the realime data to web_server')
        with open(u.REALTIME_PARSED_PATH + 'data_full.protobuf.bz2', 'rb') as full_infile, \
                open(u.REALTIME_PARSED_PATH + 'data_update.protobuf.bz2', 'rb') as update_infile:
            self.data_full = full_infile.read()
            self.data_update = update_infile.read()

        self.server.emit('new_data_full', self.data_full, namespace='/socket.io')
        self.server.emit('new_data_update', self.data_update, namespace='/socket.io')

    def server_process(self):
        eventlet.wsgi.server(eventlet.listen((u.IP, u.PORT)), self.app, log=u.server_logger, log_format=WSGI_LOG_FORMAT)

    def start(self):
        u.server_logger.info('Starting eventlet server @ %s:%s', u.IP, u.PORT)
        self.server_thread = eventlet.spawn(self.server_process)

    def stop(self):
        u.server_logger.info('Stopping eventlet server')
        self.server_thread.kill()

And, I could easily establish a websocket connection with JS from a browser:

var socket = io.connect('http://127.0.0.1:8000/socket.io');
socket.on('new_data_full', function(data) {
  console.log('got some data_full!');
  socket.emit('client_response', 'thanks!')
})
socket.on('new_data_update', function(data) {
  console.log('got some data_update!');
  socket.emit('client_response', 'thanks!')
})

But! If I attempted to create a websocket connection with another Python script, it just used polling and couldn't establish websockets:

import socketio  # type: ignore
import eventlet
import util as u  # type: ignore

eventlet.monkey_patch()

DB_SERVER_URL = f'http://{u.DB_IP}:{u.DB_PORT}'

class DatabaseClient:
    """ doc
    """
    def __init__(self):
        self.client = socketio.Client()
        self.client.on('connect', self.on_connect, namespace='/socket.io')
        self.client.on('disconnect', self.on_disconnect, namespace='/socket.io')
        self.client.on('new_data_full', self.on_new_data_full, namespace='/socket.io')
        self.client.on('new_data_update', self.on_new_data_update, namespace='/socket.io')

    def start(self):
        u.server_logger.info('~~~~~~~~~~~~~~~ Starting Database Client ~~~~~~~~~~~~~~~')
        attempt = 0
        max_attempts = 5
        while True:
            try:
                self.client.connect(DB_SERVER_URL, namespaces=['/socket.io'])
                break
            except socketio.exceptions.ConnectionError as err:
                if attempt < max_attempts:
                    attempt += 1
                    eventlet.sleep(1)
                else:
                    u.server_logger.error('Unable to connect to %s, %s', DB_SERVER_URL, err)
                    return

        eventlet.spawn(self.client.wait())
        u.server_logger.info('Connected to db_server')

    def stop(self):
        self.client.disconnect()

    def on_connect(self):
        u.server_logger.info('Connected to Database Server at %s', DB_SERVER_URL)

    def on_disconnect(self):
        u.server_logger.info('Disconnected from Database Server at %s', DB_SERVER_URL)
        # TODO: handle reconnection!

    def on_new_data_full(self, data):
        u.server_logger.info('Received new data_full')
        self.client.emit('client_response', 'Received data_full', namespace='/socket.io')

    def on_new_data_update(self, data):
        u.server_logger.info('Received new data_update')
        self.client.emit('client_response', 'Received data_update', namespace='/socket.io')

client = DatabaseClient()
client.start()

Logs:

2019-06-13 16:33:09.935 INFO 127.0.0.1,127.0.0.1 "GET /socket.io/?transport=polling&EIO=3&t=1560457989.6019762 HTTP/1.1" 200 357 0.000298
2019-06-13 16:33:09.941 INFO Client 7dbe94bde7264ac39d03d55199184f0f sent: Thanks!
2019-06-13 16:33:09.941 INFO 127.0.0.1,127.0.0.1 "POST /socket.io/?transport=polling&EIO=3&sid=7dbe94bde7264ac39d03d55199184f0f HTTP/1.1" 200 175 0.000580
2019-06-13 16:33:09.947 INFO Client 7dbe94bde7264ac39d03d55199184f0f sent: Thanks!
2019-06-13 16:33:09.947 INFO 127.0.0.1,127.0.0.1 "POST /socket.io/?transport=polling&EIO=3&sid=7dbe94bde7264ac39d03d55199184f0f HTTP/1.1" 200 175 0.000526

I eventually realized that instead of just installing:

pip install python-socketio

I should have done:

pip install python-socketio
pip install python-socketio[client]

Apparently you have to separately install the client module, which installs a package called websocket-client. It works now!

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM