I am creating a multiplayer online game, and am using socket io for the game server. Below is my server code
import socketio
import time
from aiohttp import web
sio = socketio.AsyncServer(async_mode='aiohttp', cors_allowed_origins='*', async_handlers=True)
app = web.Application()
sio.attach(app)
#sio.on('connect')
def connect(sid, environ):
print("connected: ", sid)
#sio.on('disconnect')
def disconnect(sid):
print('disconnect ', sid)
async def background_thread():
while True:
print("here")
sio.emit('message', {'data': 'Server generated event'})
time.sleep(5)
if __name__ == '__main__':
print ("Starting server")
sio.start_background_task(background_thread)
web.run_app(app, port=3000)
But the problem is the above code starts up the server but the background task is not running, where am I going wrong?
Related
I am trying to send data from server to flutter app using socketIO. Although I am able to connect and emit, the server is not able to send data to client side.
Server side code:
import cv2
import numpy as np
from flask import Flask, render_template
from flask_socketio import SocketIO, emit
from threading import Lock,Timer as tmr
from engineio.payload import Payload
import base64
import io
app = Flask(__name__)
app.config['SECRET_KEY'] = 'secret!'
socketio = SocketIO(app)
someList = ['apple', 'peas', 'juice','orange']
i=0
#socketio.on('connect')
def connect():
print("a client connected")
#socketio.on('disconnect')
def disconnect():
print('Client disconnected')
#socketio.on('msg')
def handlemsg(msg):
print (msg)
socketio.send("msg from server")
#app.route('/')
def hello():
return "hii"
if __name__ == '__main__':
socketio.run(app,host= '0.0.0.0')
Client side (flutter)
#override
void initState() {
super.initState();
IO.Socket socket = IO.io('http://x.x.x.x:5000', <String, dynamic>{
'transports': ['websocket', 'polling']});
socket.connect();
socket.emit('msg', 'test');
socket.onConnect((_) {
print('connect');
socket.emit('msg', 'testing');
});
socket.onDisconnect((_) => print('disconnect'));
socket.on('*', (data) => print(data)); //nothing is printed
}
The result I get on the server-side:
a client connected
testing
However, I get no data on the client side. Where am I going wrong? Please help
I can't test it with flutter but I tested it with client create with python-socketio
Main problem can be that send() sends message with name "message" like emit("message", ...) but your on("msg", ...) expects message with name "msg", not "message".
So you should use emit("msg", ...) in Python and on("msg", ...) in flutter.
Or you should use send() in Python and on("message", ...) in flutter.
Other problem can be that it may need some time to send message and receive it - and it may need extra time after connecting and extra time befor disconnecting - at least in my example I had to sleep to get results.
Full working code.
I added more emit() with different names.
server.py
from flask import Flask
from flask_socketio import SocketIO, emit
app = Flask(__name__)
app.config['SECRET_KEY'] = 'secret!'
socketio = SocketIO(app)
#socketio.on('connect')
def connect():
print("client connected")
#socketio.on('disconnect')
def disconnect():
print('client disconnected')
#socketio.on('question')
def handle_questio(msg):
print('question msg:', msg)
socketio.emit("answer", "msg from server")
#socketio.on('help')
def handle_help(msg):
print('help msg:', msg)
socketio.emit("support", "help from server")
#app.route('/')
def hello():
return "hii"
if __name__ == '__main__':
print('start')
socketio.run(app, host='0.0.0.0')
client.py
import socketio
sio = socketio.Client()
#sio.on('connect')
def connect():
print('connected')
#sio.on('disconnect')
def disconnect():
print('disconnected')
#sio.on('answer')
def answer(data):
print('answer:', data)
#sio.on('support')
def support(data):
print('support:', data)
# --- main ---
print('start')
sio.connect('http://localhost:5000')
print('sleep')
sio.sleep(1)
print('emit question')
sio.emit('question', {'foo': 'bar'})
print('emit help')
sio.emit('help', 'can you help me')
print('sleep')
sio.sleep(1)
sio.disconnect()
I tried to create fastapi application that uses websockets and could broadcast messages to all connected clients. I found out that it's not possible with websockets but found briliant library - socket.io. However I am not sure how could I use it and integrate it with my existing fastapi app.
# server.py
from typing import Any
import uvicorn
from fastapi import FastAPI
import socketio
sio: Any = socketio.AsyncServer(async_mode="asgi")
socket_app = socketio.ASGIApp(sio)
app = FastAPI()
#app.get("/test")
async def test():
print("test")
return "WORKS"
app.mount("/", socket_app) # Here we mount socket app to main fastapi app
#sio.on("connect")
async def connect(sid, env):
print("on connect")
#sio.on("direct")
async def direct(sid, msg):
print(f"direct {msg}")
await sio.emit("event_name", msg, room=sid) # we can send message to specific sid
#sio.on("broadcast")
async def broadcast(sid, msg):
print(f"broadcast {msg}")
await sio.emit("event_name", msg) # or send to everyone
#sio.on("disconnect")
async def disconnect(sid):
print("on disconnect")
if __name__ == "__main__":
kwargs = {"host": "0.0.0.0", "port": 5000}
kwargs.update({"debug": True, "reload": True})
uvicorn.run("server:app", **kwargs)
# client.py
import requests
import socketio
r = requests.get("http://127.0.0.1:5000/test") # server prints "test"
cl = socketio.Client()
cl2 = socketio.Client()
#cl.on("event_name")
def foo(data):
print(f"client 1 {data}")
#cl2.on("event_name")
def foo2(data):
print(f"client 2 {data}")
cl.connect("http://127.0.0.1:5000/") # server prints "on connect"
cl2.connect("http://127.0.0.1:5000/")
cl.emit("direct", "msg_1") # prints client 1 msg_1
cl2.emit("broadcast", "msg_2") # prints client 2 msg_2 and client 1 msg_2
At the end install proper dependencies:
# server.py
pip install python-socketio uvicorn fastapi
# client.py
pip install requests websocket-client
I haven't found a way to set a handler to detect when a flask server is already running. Consider the following code snippet:
import flask
import requests
def on_start():
# send a request to the server, it's safe to do so
# because we know it's already running
r = requests.get("http://localhost:1234")
print(r.text) # hello world
app = flask.Flask(__name__)
#app.route("/")
def hello():
return "hello world"
app.run(port=1234, host="localhost", on_start=on_start)
The last line fails because on_start is not an argument of run, but hopefully you get the idea of what I'm trying to do. How can I do it?
What you can do is wrap the function that you want to kick off with before_first_request decorator as found here ==> http://flask.pocoo.org/docs/1.0/api/#flask.Flask.before_first_request
However, it won't get kicked off until someone makes a request to the server but you can do something like this:
import requests
import threading
import time
from flask import Flask
app = Flask(__name__)
#app.before_first_request
def activate_job():
def run_job():
while True:
print("Run recurring task")
time.sleep(3)
thread = threading.Thread(target=run_job)
thread.start()
#app.route("/")
def hello():
return "Hello World!"
def start_runner():
def start_loop():
not_started = True
while not_started:
print('In start loop')
try:
r = requests.get('http://127.0.0.1:5000/')
if r.status_code == 200:
print('Server started, quiting start_loop')
not_started = False
print(r.status_code)
except:
print('Server not yet started')
time.sleep(2)
print('Started runner')
thread = threading.Thread(target=start_loop)
thread.start()
if __name__ == "__main__":
start_runner()
app.run()
Details & Source via Google-fu: https://networklore.com/start-task-with-flask/
I have a asycnio tcp server which is connected to 5 tcp client.
import asyncio
class EchoServerClientProtocol(asyncio.Protocol):
def connection_made(self, transport):
peername = transport.get_extra_info('peername')
print('Connection from {}'.format(peername))
self.transport = transport
def data_received(self, data):
message = data.decode()
print('Data received: {!r}'.format(message))
print('Send: {!r}'.format(message))
self.transport.write(data)
print('Close the client socket')
self.transport.close()
loop = asyncio.get_event_loop()
# Each client connection will create a new protocol instance
coro = loop.create_server(EchoServerClientProtocol, '127.0.0.1', 8888)
server = loop.run_until_complete(coro)
# Serve requests until Ctrl+C is pressed
print('Serving on {}'.format(server.sockets[0].getsockname()))
try:
loop.run_forever()
except KeyboardInterrupt:
pass
# Close the server
server.close()
loop.run_until_complete(server.wait_closed())
loop.close()
I also have a tornado websocket which is connected to 3 webbrowser.
import tornado.httpserver
import tornado.websocket
import tornado.ioloop
import tornado.web
class WSHandler(tornado.websocket.WebSocketHandler):
clients = []
def check_origin(self, origin):
return True
def open(self):
print('New Connection Established')
self.write_message("Connected Server...")
WSHandler.clients.append(self)
def on_message(self, message):
print('Message Received: {}'.format(message))
def on_close(self):
print('Connection Closed...')
WSHandler.clients.remove(self)
del self
#classmethod
def write_to_clients(cls, message):
print("Writing to Clients")
for client in cls.clients:
client.write_message(message)
#classmethod
def write_to_other_clients(cls, self_client, message):
print("Writing to Other clients")
for client in cls.clients:
if self_client != client:
client.write_message(message)
application = tornado.web.Application([
(r'/', WSHandler),
])
http_server = tornado.httpserver.HTTPServer(application)
http_server.listen(8888)
tornado.ioloop.IOLoop.instance().start()
Now I want to combine them.Here's what I want to do:
My server will always listen to my clients.
When data arrives from any of them, Program will be posted with Websocket to webbrowser asynchronously.
I did a lot of research, But couldnt reach the success...
You can use the bridge between asyncio and tornado provided by tornado:
# Imports
import asyncio
import tornado.platform
# [...]
# Define Tornado application
class WSHandler(tornado.websocket.WebSocketHandler):
clients = []
# [...]
application = tornado.web.Application([(r'/', WSHandler)])
# Define asyncio server protocol
class EchoServerClientProtocol(asyncio.Protocol):
def data_received(self, data):
WSHandler.write_to_clients(data.decode())
# [...]
# Install asyncio event loop
tornado.platform.asyncio.AsyncIOMainLoop().install()
loop = asyncio.get_event_loop()
# Create tornado HTTP server
http_server = tornado.httpserver.HTTPServer(application)
http_server.listen(8888)
# Start asyncio TCP server
coro = loop.create_server(EchoServerClientProtocol, '127.0.0.1', 8889)
server = loop.run_until_complete(coro)
# Run forever
loop.run_forever()
# [...]
I try to create a tornado websocket, which is in the background.
to handle events of input from a another device.
Usecase: Control the raspberry pi with a websocket connection from android app.
i am able to create a connection with the following implemtation:
main.py:
import sys
from sys import stdin
from websocket import connection
def main(args):
print "start application"
port = 7004
if args[0] != port:
port = args[0]
connection.WebSocketConnection(port)
core()
def core():
run = True
while run:
userinput = stdin.readline()
print userinput
if userinput == 'quit':
print "quit app"
sys.exit()
if __name__ == "__main__":
main(sys.argv)
connection handler:
import tornado.web
import message.MessageHandler
from message.messageConstructor import MessageConstructor
from tornado.web import asynchronous
class WebSocketHandler(tornado.websocket.WebSocketHandler):
def open(self):
print 'new connection'
self.send(message.MessageHandler.serialize(MessageConstructor('Engine1', '100')))
def on_message(self, rawMessage):
print rawMessage
obj = message.MessageHandler.deserialize(rawMessage)
print(obj.target)
def on_close(self):
print 'connection closed'
def check_origin(self, origin):
return True
def send(self, message):
self.write_message(message)
connection:
import tornado.httpserver
import tornado.websocket
import tornado.ioloop
import tornado.web
import socket
from websocket import connectionHandler
class WebSocketConnection():
def __init__(self, port):
self.socketHandler = connectionHandler.WebSocketHandler
application = tornado.web.Application([
(r'/', self.socketHandler),
])
http_server = tornado.httpserver.HTTPServer(application)
http_server.listen(7004)
myIP = socket.gethostbyname(socket.gethostname())
print '*** Websocket Server Started at %s***' % myIP
tornado.ioloop.IOLoop.instance().start()
def send(self, message):
self.socketHandler.send(message)
in the main.py file is function after the websocket creation and this function is never called. I guess it's a problem with the async chain of tornado.
Any suggestions?
Thanks in advance.
i figured out a way to do that, but i am not sure if this is the right way. Anyway in this set up it works:
main.py:
import sys
from sys import stdin
from websocket import connection
from tornado.ioloop import IOLoop
def main(args):
print "start application"
port = 7004
if args[0] != port:
port = args[0]
connection.WebSocketConnection(port)
core()
IOLoop.instance().start()
def core():
run = True
while run:
userinput = stdin.readline()
print userinput
if userinput == 'quit':
print "quit app"
sys.exit()
if __name__ == "__main__":
main(sys.argv)
connection.py:
import tornado.httpserver
import tornado.websocket
import tornado.ioloop
import tornado.web
import socket
from websocket import connectionHandler
class WebSocketConnection():
def __init__(self, port):
self.socketHandler = connectionHandler.WebSocketHandler
application = tornado.web.Application([
(r'/', self.socketHandler),
])
http_server = tornado.httpserver.HTTPServer(application)
http_server.listen(7004)
myIP = socket.gethostbyname(socket.gethostname())
print '*** Websocket Server Started at %s***' % myIP
def send(self, message):
self.socketHandler.send(message)
as you can see i start the loop just in the main file after calling the core function. In this way it works.
but as i said before i am not sure if this i really correct.