背景
这个项目的目的是为我在本地运行的程序创建一个基于 SMS 的终止开关。该计划是在本地程序和 Heroku 上托管的应用程序之间创建 Web 套接字连接。使用 Twilio,接收和 SMS 将触发对此应用程序的 POST 请求。如果它来自我的白名单上的号码,应用程序应该向本地程序发送命令以关闭。
问题
我可以做些什么来找到对命名空间的引用,以便我可以从 POST 请求向所有连接的客户端广播消息?
现在我只是创建一个新的 Web 套接字客户端,连接它并发送消息,因为我似乎无法弄清楚如何以一种可以调用发射或广播的方式访问命名空间对象。
服务器代码
from gevent import monkey
from flask import Flask, Response, render_template, request
from socketio import socketio_manage
from socketio.namespace import BaseNamespace
from socketio.mixins import BroadcastMixin
from time import time
import twilio.twiml
from socketIO_client import SocketIO #only necessary because of the hack solution
import socketIO_client
monkey.patch_all()
application = Flask(__name__)
application.debug = True
application.config['PORT'] = 5000
# White list
callers = {
"+15555555555": "John Smith"
}
# Part of 'hack' solution
stop_namespace = None
socketIO = None
# Part of 'hack' solution
def on_connect(*args):
global stop_namespace
stop_namespace = socketIO.define(StopNamespace, '/chat')
# Part of 'hack' solution
class StopNamespace(socketIO_client.BaseNamespace):
def on_connect(self):
self.emit("join", 'server@email.com')
print '[Connected]'
class ChatNamespace(BaseNamespace, BroadcastMixin):
stats = {
"people" : []
}
def initialize(self):
self.logger = application.logger
self.log("Socketio session started")
def log(self, message):
self.logger.info("[{0}] {1}".format(self.socket.sessid, message))
def report_stats(self):
self.broadcast_event("stats",self.stats)
def recv_connect(self):
self.log("New connection")
def recv_disconnect(self):
self.log("Client disconnected")
if self.session.has_key("email"):
email = self.session['email']
self.broadcast_event_not_me("debug", "%s left" % email)
self.stats["people"] = filter(lambda e : e != email, self.stats["people"])
self.report_stats()
def on_join(self, email):
self.log("%s joined chat" % email)
self.session['email'] = email
if not email in self.stats["people"]:
self.stats["people"].append(email)
self.report_stats()
return True, email
def on_message(self, message):
message_data = {
"sender" : self.session["email"],
"content" : message,
"sent" : time()*1000 #ms
}
self.broadcast_event_not_me("message",{ "sender" : self.session["email"], "content" : message})
return True, message_data
@application.route('/stop', methods=['GET', 'POST'])
def stop():
'''Right here SHOULD simply be Namespace.broadcast("stop") or something.'''
global socketIO
if socketIO == None or not socketIO.connected:
socketIO = SocketIO('http://0.0.0.0:5000')
socketIO.on('connect', on_connect)
global stop_namespace
if stop_namespace == None:
stop_namespace = socketIO.define(StopNamespace, '/chat')
stop_namespace.emit("join", 'server@bayhill.com')
stop_namespace.emit('message', 'STOP')
return "Stop being processed."
@application.route('/', methods=['GET'])
def landing():
return "This is Stop App"
@application.route('/socket.io/<path:remaining>')
def socketio(remaining):
try:
socketio_manage(request.environ, {'/chat': ChatNamespace}, request)
except:
application.logger.error("Exception while handling socketio connection",
exc_info=True)
return Response()
我从这个项目chatzilla大量借用了代码,这无疑是非常不同的,因为我并没有真正使用浏览器。
也许 Socketio 对于 web 套接字来说是一个糟糕的选择,我应该使用 Tornado,但这看起来效果很好,而且这个设置帮助我轻松地将 REST 和 web 套接字部分分开