Issue
I am building a webserver application with aiohttp and python. For the exchange of data I'm using the socketio python implementation. For getting data from my frontend to my python script everything works as expected. Now I want to send some data from my python script to display it in the browser. For that reason I want to implement a function that is emitting the given data.
When i try to call socket_io.emit('data',"test")
directly i get a runtime warning:
RuntimeWarning: coroutine 'AsyncServer.emit' was never awaited > my_server.socket_io.emit('data', "test") RuntimeWarning: Enable tracemalloc to get the object allocation traceback
I already did some research and this is normal i think.
When i create a async function definiton like this:
async def sendData(dataOut):
await socket_io.emit('dataOut', dataOut)
no message is emitted
This is my python testcode:
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
And the "public" HTML file used:
<!-- 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>
How can i create a function that is emitting given data when i call it?
EDIT:
New python script to call the emit function from another thread:
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')
Steps to reproduce:
- Start the Server, no Client connected:
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
- Connect via browser
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
Solution
The problem is in the last two lines of your script:
web.run_app(app, host='XXX.XXX.XXX.XXX', port='XXXX')
sendData(dataOut) #is not doing anything
The web.run_app()
call is blocking, it starts the web server and then enters a listening state, without ever returning (unless the web server is terminated). So the second line never gets a chance to execute.
So what you need to do is find a better time to call sendData()
, ideally after one or more clients are connected to the server already. One option is to call it from a client event handler, in your case that would be either the connect_handler()
or dataInHandler()
functions. Another option is to start a background task before you start the web server that somehow waits for the appropriate moment to emit the data.
Response to your edit:
You are mixing asyncio and threads, and that does not work. Or actually, you cannot have a SocketIO server in use across threads, all usages must be in the same thread as the asyncio loop. If you need a background task, use a coroutine. The Socket.IO repository has an example of a background task that is started with the socket_io.start_background_task()
function.
Answered By - Miguel
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.