1

问题

使用带有 Python 3.7 的 eventlet 0.25.0 和 python-socketio 4.0.3,我试图让两个 Python 脚本与 websocket 通信。服务器脚本运行良好:

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()

而且,我可以从浏览器轻松地与 JS 建立 websocket 连接:

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!')
})

但!如果我尝试使用另一个 Python 脚本创建 websocket 连接,它只是使用轮询并且无法建立 websocket:

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()

日志:

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
4

1 回答 1

4

我最终意识到,不仅仅是安装:

pip install python-socketio

我应该这样做:

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

显然您必须单独安装客户端模块,该模块安装了一个名为 websocket-client 的包。现在可以了!

于 2019-06-17T16:00:32.680 回答