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?
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 am trying to have a Flask Server that allows me to launch a tweepy stream and on every message received in the stream listener, it sends that message to a socketio client. The Flask server is at the same time supposed to allow Twilio to post to it, and route that message to the client—so that the client is receiving messages from both Twilio and twitter.
I have been trying to get the server to send messages over to the client for the data incoming from twitter, the code for Twilio works just fine. It sends data over to the client on message receipt. The main loop in tweepy is also not locking up the program—I can test print statements and see tweets and the incoming sms's being printed in the handle_message(msg) function asynchronously. I feel like there must be something really simple that I am missing here since the SMS's are emitted to the client, but the incoming tweets are not, even though they are propagating through to the handle_message(msg) function. What gives?
server.py
from flask import Flask, json, request
from twilio.twiml.messaging_response import Message, MessagingResponse
from flask_socketio import SocketIO
import tweepy
import json
PATH = '/path/to/credentials/'
with open(PATH, "r") as file:
credentials = json.load(file)
app = Flask(__name__)
app.debug = True
app.config['SECRET_KEY'] = 'abc123'
sio = SocketIO(app, cors_allowed_origins="*")
auth = tweepy.OAuthHandler(credentials['CONSUMER_KEY'], credentials['CONSUMER_SECRET'])
auth.set_access_token(credentials['ACCESS_TOKEN'], credentials['ACCESS_SECRET'])
api = tweepy.API(auth)
class MyListener(tweepy.StreamListener):
def on_status(self, status):
print('status')
def on_data(self, data):
handle_message(data)
def on_error(self, status):
print('error')
print(status)
stream_listener = MyListener()
# twilio sms route
#app.route('/sms', methods=['POST'])
def sms():
number = request.form['From']
message_body = request.form['Body']
message_data = {"number": number, "msg": message_body}
resp = MessagingResponse()
resp.message('Hello {}, you said: {}'.format(number, message_body))
handle_message(message_data)
return str(resp)
# flask-socketio stuff
#sio.on('connect')
def connect():
print('connected')
sio.emit('client_connected', "you connected")
search_term = "#mysearchterm"
stream = tweepy.Stream(auth=api.auth, listener=stream_listener)
stream.filter(track=[search_term], is_async=True)
sio.emit('client_connected', "the search term is {}".format(search_term))
#sio.on('disconnect')
def disconnect():
print('Client Diconnected')
#sio.event
def handle_message(message):
print("This is the message received: ", message)
sio.emit('handle_message', message)
if __name__ == '__main__':
sio.run(app)
client.py
import socketio
client = socketio.Client()
#client.on('client_connected')
def on_connect(message):
print(message)
#client.on('handle_message')
def message(data):
print(data)
client.connect('http://localhost:5000/')
Twilio developer evangelist here.
You've decorated the handle_message function as #sio.event but as far as I can see in the docs, you should only do that to have the handle_message method respond to events on the socket called "handle_message".
I'd start be removing the #sio.event decorator.
I'm not a Python expert, but I also wonder whether there is a scope issue here. You define your MyListener class and create an instance of it before you define the handle_message method. Just to test, can you try emitting to the socket directly within the on_data method:
def on_data(self, data):
sio.emit('handle_message', data)
If that works, consider moving the definition of handle_message above the definition of MyListener.
I solved my problem! As I noted in this comment, the issue was with multithreading and passing information between the threads. With tweepy, the parameter is_async=True, which in 4.1.0 is threading=True, opens up a new thread once the stream is run.
Instead of trying to deal with passing information around, I exploited the extant flask-socketio functionality by using a local redis server as a message queue (start from the section "Using Multiple Workers" if you are setting this up for the first time, also be sure to install redis).
Here is the updated server.py code. The client.py code remained essentially unchanged:
import eventlet
eventlet.monkey_patch()
from flask import Flask, json, request
from twilio.twiml.messaging_response import Message, MessagingResponse
from flask_socketio import SocketIO
import tweepy
import json
PATH = '/PATH/TO/CREDENTIALS'
with open(PATH, "r") as file:
credentials = json.load(file)
app = Flask(__name__)
app.debug = True
app.config['SECRET_KEY'] = 'abc123'
sio = SocketIO(app, message_queue='redis://', cors_allowed_origins="*")
class MyStream(tweepy.Stream):
def __init__(self, consumer_key, consumer_secret, access_token, access_secret):
super(MyStream, self).__init__(consumer_key, consumer_secret, access_token, access_secret)
self.stream_sio = SocketIO(message_queue='redis://')
def on_status(self, status):
print('status')
def on_data(self, data):
json_data = json.loads(data)
self.stream_sio.emit('handle_message', json_data['text'])
# TODO: Send along all necessary information
#app.route('/sms', methods=['POST'])
def sms():
number = request.form['From']
message_body = request.form['Body']
message_data = {"number": number, "msg": message_body}
resp = MessagingResponse()
resp.message('Hello {}, you said: {}'.format(number, message_body))
handle_message(message_data)
return str(resp)
#sio.on('connect')
def connect():
print('connected')
sio.emit('client_connected', "you connected")
search_term = "#testingtesting123"
stream = MyStream(credentials['CONSUMER_KEY'], credentials['CONSUMER_SECRET'],
credentials['ACCESS_TOKEN'], credentials['ACCESS_SECRET'])
stream.filter(track=[search_term], threaded=True)
sio.emit('client_connected', "the search term is {}".format(search_term))
#sio.on('disconnect')
def disconnect():
print('Client disconnected')
def handle_message(message):
sio.emit('handle_message', message)
if __name__ == '__main__':
sio.run(app)
I'm trying to use Flask and SocketIO, i found this example where some random numbers are printed dynamically in real time on a webpage.
from flask_socketio import SocketIO, emit
from flask import Flask, render_template, url_for, copy_current_request_context
from random import random
from time import sleep
from threading import Thread, Event
__author__ = 'slynn'
app = Flask(__name__)
app.config['SECRET_KEY'] = 'secret!'
app.config['DEBUG'] = True
#turn the flask app into a socketio app
socketio = SocketIO(app)
#random number Generator Thread
thread = Thread()
thread_stop_event = Event()
class RandomThread(Thread):
def __init__(self):
self.delay = 1
super(RandomThread, self).__init__()
def randomNumberGenerator(self):
"""
Generate a random number every 1 second and emit to a socketio instance (broadcast)
Ideally to be run in a separate thread?
"""
#infinite loop of magical random numbers
print("Making random numbers")
while not thread_stop_event.isSet():
number = round(random()*10, 3)
print(number)
socketio.emit('newnumber', {'number': number}, namespace='/test')
sleep(self.delay)
def run(self):
self.randomNumberGenerator()
#app.route('/')
def index():
#only by sending this page first will the client be connected to the socketio instance
return render_template('index.html')
#socketio.on('connect', namespace='/test')
def test_connect():
# need visibility of the global thread object
global thread
print('Client connected')
#Start the random number generator thread only if the thread has not been started before.
if not thread.isAlive():
print("Starting Thread")
thread = RandomThread()
thread.start()
#socketio.on('disconnect', namespace='/test')
def test_disconnect():
print('Client disconnected')
if __name__ == '__main__':
socketio.run(app)
I have a Python script which connects to a Websocket, receives data and prints this data.
import websocket
from bitmex_websocket import Instrument
from bitmex_websocket.constants import InstrumentChannels
from bitmex_websocket.constants import Channels
import json
websocket.enableTrace(True)
channels = [
InstrumentChannels.trade,
]
XBTUSD = Instrument(symbol='XBTUSD',
channels=channels)
XBTUSD.on('action', lambda msg: test(msg))
def test(msg):
parsed = json.loads(json.dumps(msg))
print(parsed)
XBTUSD.run_forever()
I now want to print this data on my webpage, i tried to "merge" these two scripts but apparently i can't, because to run the Websocket connector i have to run XBTUSD.run_forever(), which will start the process but it won't run the rest of the code, including the Flask part which generates the webpage, so only one at time can run.
Is there a way to make the Websocket connector communicate with the Flask/Socketio part so that the data from the Websocket connector is printed on the webpage?
I'm trying to make a Flask app that uses WebSockets. The example from Flask-sockets works but how would I send a message from a regular view?
Similarly to how Flask-SocketIO use .emit() and .send()-methods.
In the example below (from the Flask-Sockets example) I would for instance like to be able to broadcast a message from the hello-view.
from flask import Flask
from flask_sockets import Sockets
app = Flask(__name__)
sockets = Sockets(app)
#sockets.route('/echo')
def echo_socket(ws):
while not ws.closed:
message = ws.receive()
ws.send(message)
#app.route('/')
def hello():
# How can I send a WebSocket message from here?
return 'Hello World!'
if __name__ == "__main__":
from gevent import pywsgi
from geventwebsocket.handler import WebSocketHandler
server = pywsgi.WSGIServer(('', 5000), app, handler_class=WebSocketHandler)
server.serve_forever()
You can use a global socket list of all client. Traverse all list and send message to all ws instance.
Example code;
from flask import Flask, render_template
from flask_sockets import Sockets
app = Flask(__name__)
sockets = Sockets(app)
ws_list = []
#sockets.route('/echo')
def echo_socket(ws):
ws_list.append(ws)
while not ws.closed:
message = ws.receive()
ws.send(message)
#app.route('/')
def hello():
# How can I send a WebSocket message from here?
return render_template('index.html')
#app.route('/send_message_to_all_client')
def broadcast():
for ws in ws_list:
if not ws.closed:
ws.send("broadcast message")
else:
# Remove ws if connection closed.
ws_list.remove(ws)
return "ok"
if __name__ == "__main__":
from gevent import pywsgi
from geventwebsocket.handler import WebSocketHandler
server = pywsgi.WSGIServer(('', 5000), app, handler_class=WebSocketHandler)
server.serve_forever()