вызов python socket_io.emit () из функции

Я создаю приложение веб-сервера с aiohttp и python. Для обмена данными я использую реализацию python socketio. Для получения данных из моего интерфейса в мой скрипт python все работает, как ожидалось. Теперь я хочу отправить некоторые данные из моего скрипта Python, чтобы отобразить их в браузере. По этой причине я хочу реализовать функцию, которая генерирует заданные данные.

Когда я пытаюсь вызвать socket_io.emit('data',"test") напрямую, я получаю предупреждение во время выполнения:

RuntimeWarning: сопрограмма 'AsyncServer.emit' никогда не ожидалась> my_server.socket_io.emit ('data', «test») RuntimeWarning: включить tracemalloc для получения трассировки выделения объекта

Я уже провел небольшое исследование, и я думаю, это нормально.

Когда я создаю определение асинхронной функции следующим образом:

async def sendData(dataOut):
    await socket_io.emit('dataOut', dataOut) 

сообщение не отправляется


Это мой тестовый код на Python:

from aiohttp import web
import socketio

socket_io = socketio.AsyncServer(async_mode='aiohttp')
app = web.Application()
socket_io.attach(app)

app.router.add_static('/', path=str('public/'))


configData = "testConfig"
dataOut ="testOut"
dataBack ="testBack"


async def index(request):
    with open('public/index.html') as f:
        return web.Response(text=f.read(), content_type='text/html')

app.router.add_get('/', index)   

@socket_io.on('connect')
async def connect_handler(sid, environ): 
    print("new connection") # works as expected
    await socket_io.emit('initial_config', configData) # works as expected

@socket_io.on("dataIn")
async def dataInHandler(sid, data):
    print("new data") # works as expected
    await socket_io.emit('dataBack', dataBack) # works as expected

async def sendData(dataOut):
    await socket_io.emit('dataOut', dataOut)  

web.run_app(app, host='XXX.XXX.XXX.XXX', port='XXXX')    

sendData(dataOut) #is not doing anything 

И использовался «общедоступный» HTML-файл:

<!-- index.html -->
<!DOCTYPE html>
<html lang="en">
    <head>
        <title>socketio Test</title>
    </head>
    <body>
        <h1>socketio Test</h1>

        <input type="checkbox" value="0" onClick="emit(id)" id="IN1"></input>

        <script src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/2.2.0/socket.io.js"></script>
        <script>

            const socket = io("http://XXX.XXX.XXX.XXX:XXXX");
            function emit(id){
                channel_obj = eval(id)
                console.log(id, typeof(id), channel_obj.value, typeof(channel_obj.value))
                if (channel_obj.value == 1){
                    channel_obj.value = 0
                }
                else{
                    channel_obj.value = 1
                }
                socket.emit("dataIn", id+":"+channel_obj.value);
            }               

            socket.on("initial_config", function(data) {
                console.log(data);
            }); 

            socket.on("dataBack", function(data) {
                console.log(data);
            }); 

            socket.on("dataOut", function(data) {
                console.log(data);
            }); 

        </script>
    </body>
</html>

Как я могу создать функцию, которая выдает данные, когда я ее вызываю?

РЕДАКТИРОВАТЬ:

Новый скрипт python для вызова функции emit из другого потока:

from aiohttp import web
import socketio, threading, time

configData = "testConfig"
dataOut ="testOut"
dataBack ="testBack"
data_flag = 0
connection_flag = 0


print("setup web-server")
socket_io = socketio.AsyncServer(async_mode='aiohttp')
app = web.Application()
socket_io.attach(app)

app.router.add_static('/', path=str('public/'))

async def index(request):
    with open('public/index.html') as f:
        return web.Response(text=f.read(), content_type='text/html')

app.router.add_get('/', index)   

@socket_io.on('connect')
async def connect_handler(sid, environ): 
    global connection_flag
    print("new connection") # works as expected
    connection_flag = 1
    await socket_io.emit('initial_config', configData) # works as expected

@socket_io.on("dataIn")
async def dataInHandler(sid, data):
    print("new data") # works as expected
    data_flag = 1
    await socket_io.emit('dataBack', dataBack) # works as expected

async def sendData(dataOut):
    await socket_io.emit('dataOut', dataOut)  

def main():
    global connection_flag
    try:
        print("in main loop")
        time.sleep(1)
        print("wait till a client connects")
        while connection_flag == 0:
            pass
        print("wait 5 seconds")    
        time.sleep(5)
        i = 0
        while True:
            print("now emitting: ", i)
            sendData(i)
            i += 1
            time.sleep(1)

    finally:
        thread.join() 
        print("finished, exiting now")

thread = threading.Thread(target=main, args=())
thread.daemon=True 
thread.start()
print("starting web-server")
web.run_app(app, host='192.168.132.210', port='5000')  

Действия по воспроизведению:

  1. Запустите сервер, клиент не подключен:
setup web-server
in main loop
starting web-server
======== Running on http://192.168.132.210:5000 ========
(Press CTRL+C to quit)
wait till a client connects
  1. Подключиться через браузер
new connection
wait 5 seconds
now emitting:  0
aiohttp_thread.py:53: RuntimeWarning: coroutine 'sendData' was never awaited
  sendData(i)
RuntimeWarning: Enable tracemalloc to get the object allocation traceback
now emitting:  1
now emitting:  2
now emitting:  3
now emitting:  4
now emitting:  5

person Moritz Vierneusel    schedule 16.10.2019    source источник


Ответы (2)


Проблема в последних двух строках вашего скрипта:

web.run_app(app, host='XXX.XXX.XXX.XXX', port='XXXX')    

sendData(dataOut) #is not doing anything

Вызов web.run_app() является блокирующим, он запускает веб-сервер, а затем переходит в состояние прослушивания без возврата (если веб-сервер не завершен). Таким образом, у второй строки никогда не будет шанса выполнить.

Итак, что вам нужно сделать, так это найти лучшее время для вызова sendData(), в идеале после того, как один или несколько клиентов уже подключены к серверу. Один из вариантов - вызвать его из клиентского обработчика событий, в вашем случае это будут функции connect_handler() или dataInHandler(). Другой вариант - запустить фоновую задачу до того, как вы запустите веб-сервер, который каким-то образом ждет подходящего момента для передачи данных.

Ответ на ваше изменение:

Вы смешиваете asyncio и потоки, и это не работает. Или на самом деле у вас не может быть сервера SocketIO, используемого в потоках, все использования должны быть в том же потоке, что и цикл asyncio. Если вам нужна фоновая задача, используйте сопрограмму. В репозитории Socket.IO есть пример фоновой задачи, которая запускается с помощью функции socket_io.start_background_task().

person Miguel    schedule 16.10.2019
comment
Я заметил, что вызов emit из обработчика событий отлично выполняет свою работу, а также что web.run_app() блокируется, но когда я пытаюсь вызвать свою функцию sendData() из другого потока (который запускается до того, как я вызываю web.run_app(), и выполняет команду после запуска сервера и работает), я получаю указанное выше RuntimeWarning, и мое сообщение тоже не отправляется. Так есть ли возможность вызвать излучение из другого потока? Я заметил, что вы разработчик socketio для python и, возможно, у вас есть ответ. Я не нашел ничего по этой теме в документации. - person Moritz Vierneusel; 17.10.2019
comment
Да, вы можете излучать из фонового потока. Предупреждение, которое вы показываете, не согласуется с кодом, который вы представили позже, поэтому я отвечаю на основе кода, который вы представили. Пожалуйста, обновите свой вопрос кодом, который генерирует это предупреждение. - person Miguel; 17.10.2019
comment
Обновлен вопрос, чтобы лучше продемонстрировать мою проблему - person Moritz Vierneusel; 17.10.2019

Tanks to Miguel Мне удалось исправить свой код, поэтому без потоковой передачи, вместо этого запустите фоновую задачу с помощью socketio:


from aiohttp import web
import socketio, time, asyncio

configData = "testConfig"
dataOut ="testOut"
dataBack ="testBack"
data_flag = 0
connection_flag = 0

print("setup web-server")
socket_io = socketio.AsyncServer(async_mode='aiohttp')
app = web.Application()
socket_io.attach(app)

app.router.add_static('/', path=str('public/'))

async def index(request):
    with open('public/index.html') as f:
        return web.Response(text=f.read(), content_type='text/html')

app.router.add_get('/', index)   

@socket_io.on('connect')
async def connect_handler(sid, environ): 
    global connection_flag
    print("new connection") # works as expected
    connection_flag = 1
    await socket_io.emit('initial_config', configData) # works as expected

@socket_io.on("dataIn")
async def dataInHandler(sid, data):
    print("new data") # works as expected
    data_flag = 1
    await socket_io.emit('dataBack', dataBack) # works as expected

async def sendData(dataOut):
    await socket_io.emit('dataOut', dataOut)  

async def main():
    global connection_flag
    try:
        print("in main loop")
        await asyncio.sleep(1)
        print("wait till a client connects")
        while connection_flag == 0:
            pass
        print("wait 5 seconds")    
        await asyncio.sleep(5)
        i = 0
        while True:
            print("now emitting: ", i)
            #await sendData(i)
            await socket_io.emit('dataOut', i) 
            i += 1
            await asyncio.sleep(1)

    finally: 
        print("finished, exiting now")

socket_io.start_background_task(main)
print("starting web-server")
web.run_app(app, host='192.168.132.210', port='5000')  
person Moritz Vierneusel    schedule 18.10.2019