Is it possible, if multiple socket clients are connected to a tornado websocket server, to send a message to a specific one?
I don't know if there is a possibility of getting a client's id and then send a message to that id.
My client code is:
from tornado.ioloop import IOLoop, PeriodicCallback
from tornado import gen
from tornado.websocket import websocket_connect
class Client(object):
def __init__(self, url, timeout):
self.url = url
self.timeout = timeout
self.ioloop = IOLoop.instance()
self.ws = None
self.connect()
PeriodicCallback(self.keep_alive, 20000, io_loop=self.ioloop).start()
self.ioloop.start()
#gen.coroutine
def connect(self):
print "trying to connect"
try:
self.ws = yield websocket_connect(self.url)
except Exception, e:
print "connection error"
else:
print "connected"
self.run()
#gen.coroutine
def run(self):
while True:
msg = yield self.ws.read_message()
print msg
if msg is None:
print "connection closed"
self.ws = None
break
def keep_alive(self):
if self.ws is None:
self.connect()
else:
self.ws.write_message("keep alive")
if __name__ == "__main__":
client = Client("ws://xxx", 5 )
When your client connects to the websocket server will be invoked method 'open' on WebSocketHandler, in this method you can keep socket in the Appliaction.
Like this:
from tornado import web
from tornado.web import url
from tornado.websocket import WebSocketHandler
class Application(web.Application):
def __init__(self):
handlers = [
url(r"/websocket/server/(?P<some_id>[0-9]+)/", WebSocketServer),
]
web.Application.__init__(self, handlers)
self.sockets = {}
class WebSocketServer(WebSocketHandler):
def open(self, some_id):
self.application.sockets[some_id] = self
def on_message(self, message):
self.write_message(u"You said: " + message)
def on_close(self):
print("WebSocket closed")
You also may use message for connection, in this message you have to tell socket id and save socket into the Application.
Related
Im creating an application to receive data from multiple iot devices
Each iot device has a socket server
so i need to create multi socket client
Im using Twisted library for create multiple socket client
Problem is that my application can't detect connection failure and it wont try for reconnection
Herer is my code:
import time
import requests
from twisted.internet.protocol import Protocol, ReconnectingClientFactory as rcf, connectionDone
from twisted.internet import reactor
from twisted.internet.endpoints import TCP4ClientEndpoint
from twisted.python import failure
client_list = [("device ip", device_port)]
endpoints = {}
class Client(Protocol):
"""
when receive a massage call this function
"""
def connectionMade(self):
self.transport.setTcpKeepAlive(True)
def dataReceived(self, data):
reactor.callInThread(self._dataReceived, data)
def _dataReceived(self, data):
if self.connected == 1:
data = data[:-1].decode("utf-8")
data = data.split('\n')
for tag in data:
reactor.callInThread(self._push_data, tag, self.transport.addr[0])
def _push_data(self, data, ip):
print("start pushing")
print(data[1:-1])
try:
pass
// send a web request
except Exception as xp:
print(xp, end="\n")
class ClientFactory(rcf):
def buildProtocol(self, addr):
print(addr, 'Connected.')
return Client()
if __name__ == "__main__":
for client in client_list:
endpoint = TCP4ClientEndpoint(reactor, client[0], client[1])
endpoints[client] = endpoint
endpoint.connect(ClientFactory())
reactor.run()
i writing some code for fix this problem but that's not worked
import time
import requests
from twisted.internet.protocol import Protocol, ReconnectingClientFactory as clf, connectionDone
from twisted.internet import reactor
from twisted.internet.endpoints import TCP4ClientEndpoint
from twisted.python import failure
client_list = [("device ip", device_port)]
endpoints = {}
class Client(Protocol):
"""
when receive a massage call this function
"""
def dataReceived(self, data):
reactor.callInThread(self._dataReceived, data)
def _dataReceived(self, data):
if self.connected == 1:
data = data[:-1].decode("utf-8")
data = data.split('\n')
for tag in data:
reactor.callInThread(self._push_data, tag, self.transport.addr[0])
def _push_data(self, data, ip):
print("start pushing")
print(data[1:-1])
try:
pass
// send a web request
except Exception as xp:
print(xp, end="\n")
def connectionLost(self, reason: failure.Failure = connectionDone):
try:
endpoints.pop(self.transport.addr[0])
except Exception as xp:
print(xp)
print(f"Protocol client {self.transport.addr[0]} connectionLost \n ", reason)
status = True
while status:
try:
endpoint = TCP4ClientEndpoint(reactor, self.transport.addr[0], 100)
endpoints[client] = endpoint
endpoint.connect(ClientFactory())
status = False
except Exception as xp:
print(xp)
class ClientFactory(clf):
def buildProtocol(self, addr):
print(addr, 'Connected.')
return Client()
def clientConnectionFailed(self, connector, reason):
print("can't start connect to the server")
print(reason)
clf.clientConnectionFailed(self, connector, reason)
def clientConnectionLost(self, connector, reason):
print(reason)
clf.clientConnectionLost(self, connector, reason)
if __name__ == "__main__":
for client in client_list:
endpoint = TCP4ClientEndpoint(reactor, client[0], client[1])
endpoints[client] = endpoint
endpoint.connect(ClientFactory())
reactor.run()
my question is how i can set an interval ping for check connection
I am working on Rabbitmq and Server sent events for this I use tornado-eventsource (https://github.com/guilhermef/tornado-eventsource/tree/master/tornado_eventsource) from this code I tried to make my connection of EventSource. When my server receives messages it suooise to send it to client but it gives this error message and messages are not watched by server to send
But I guess due to a change in versions as I use python3 I get an error.
File "C:\Users\Admin\AppData\Local\Programs\Python\Python37\lib\site-packages\tornado_eventsource\handler.py", line 71, in write_message
if isinstance(msg, str) or isinstance(msg, unicode):
NameError: name 'unicode' is not defined
Please guide me regards this.
Server side code here:
import _thread
import asyncio
import time
import os
import tornado.ioloop
import tornado.web
from DateTime import DateTime
import pika
from tornado.web import Application, RequestHandler
from threading import Thread
import logging
import six
#from tornadose.handlers
import tornado_eventsource.handler
from tornado_eventsource.handler import EventSourceHandler
logging.basicConfig(level=logging.INFO)
# web socket clients connected.
clients = []
connection = pika.BlockingConnection()
logging.info('Connected:localhost')
channel = connection.channel()
print("Connection Time: ", time.time()*1000)
print("Connection Time: ", DateTime())
def get_connection():
credentials = pika.PlainCredentials('guest', 'guest')
conn = pika.BlockingConnection(pika.ConnectionParameters(host="localhost", port=5672,
virtual_host="/",
credentials=credentials))
return conn
def callback(ch, method, properties, body):
print(" [x] Received %r", fetch_DateTime(), ':', body)
time.sleep(body.count(b'.'))
#print(fetch_DateTime())
for itm in clients:
itm.write_message(body)
def start_consumers():
asyncio.set_event_loop(asyncio.new_event_loop())
channel = get_connection().channel()
channel.queue_declare(queue="my_queue")
channel.basic_consume(
queue="my_queue",
on_message_callback=callback,
auto_ack=True)
channel.start_consuming()
def fetch_DateTime():
time_milliseconds = time.time() * 1000
return(time_milliseconds)
def disconnect_to_rabbitmq():
channel.stop_consuming()
connection.close()
logging.info('Disconnected from Rabbitmq')
class EventHandler(tornado_eventsource.handler.EventSourceHandler):
def open(self):
logging.info('Server-Sent Events opened')
clients.append(self)
def on_close(self):
logging.info('Server-Sent Events closed')
clients.remove(self)
class MainHandler(tornado.web.RequestHandler):
def get(self):
self.render("SSE_client.html")
def make_app():
return tornado.web.Application([
(r'/', EventHandler,),
(r"/", MainHandler),
])
class WebServer(tornado.web.Application):
def __init__(self):
handlers = [ (r'/', EventHandler,),
(r"/", MainHandler), ]
settings = {'debug': True}
super().__init__(handlers, **settings)
def run(self, port=8888):
self.listen(port)
tornado.ioloop.IOLoop.instance().start()
class TestHandler(tornado.web.RequestHandler):
def get(self):
self.write("test success")
ws = WebServer()
def start_server():
asyncio.set_event_loop(asyncio.new_event_loop())
ws.run()
if __name__ == "__main__":
logging.info('Starting thread Tornado')
threadC = Thread(target=start_consumers)
threadC.start()
from threading import Thread
t = Thread(target=start_s
erver, args=())
t.daemon = True
t.start()
t.join()
try:
input("Server ready. Press enter to stop\n")
except SyntaxError:
pass
try:
logging.info('Disconnecting from RabbitMQ..')
disconnect_to_rabbitmq()
except Exception:
pass
stopTornado();
logging.info('See you...')
Support to Python 3.6+ was added on the version 2.0.0rc2
Have an app that is using (Python 3.6) Tkinter & Tornado. Would like it send a websocket message when a button is pressed.
The sendSocket is in my class that handles the interface. I am able to open my sockets ok, and can send data into the socket handler ok. Additionally, it serves up my html file ok from my RequestHandler.
I can see that my code hits the sendSocketMessage line ok. However, I never get the print from within the SocketHandler.send_message def. There are no errors in the console.
def sendSocketMessage(self, data = "whatever"):
print("sending")
#WebSocketeer.send_message(data)
ioloop.IOLoop.current().add_callback(WebSocketeer.send_message, data)
class WebSocketeer(websocket.WebSocketHandler):
def open(self):
print("WebSocket opened")
def on_message(self, message):
print("got message: " + message)
def on_close(self):
print("WebSocket closed")
#classmethod
def send_message(self, message):
print("sending message: " + message)
for session_id, session in self.session.server._sessions._items.iteritems():
session.conn.emit(event, message)
Code based off of these SO responses
Send a websocket message:
How do I send a websocket message in Tornado at will?
Send to all clients:
Is it possible to send a message to all active WebSocket connections? Using either node.js or python tornado websockets
Found a way to make it work here: How to run functions outside websocket loop in python (tornado)
But am still wondering why the add_callback doesn't work - as, from what I've read, this is the recommended way.
This is what I've got working, taken from: https://github.com/tornadoweb/tornado/issues/2802
clients = [];
class WSHandler(tornado.websocket.WebSocketHandler):
def open(self):
print('connection opened...')
clients.append(self);
def on_message(self, message):
self.write_message("The server says: " + message + " back at you")
print('received:', message)
def on_close(self):
clients.remove(self);
print('connection closed...')
#classmethod
def send_message(self, message):
print("sending message: " + message)
for client in clients:
client.write_message(message);
#for session_id, session in self.session.server._sessions._items.iteritems():
# session.conn.emit(event, message);
return True;
def sendRandom():
global thread, data;
try:
print("sendRandom()");
time.sleep(0.125);
n = random.randint(0,1000);
data = str(n);
data = {"msg":"data","data":data};
if eventLoop is not None:
#If response needed
#sendData(eventLoop,WSHandler.send_message,json.dumps(data));
#else
eventLoop.add_callback(WSHandler.send_message,json.dumps(data));
except:
print("Err");
traceback.print_exc();
clients = [];
def sendData(loop,f,*a,**kw):
print("%s %s" % (type(loop),type(f)));
concurrent_future = concurrent.futures.Future();
async def wrapper():
try:
rslt = f(*a,**kw);
except Exception as e:
concurrent_future.set_exception(e);
else:
concurrent_future.set_result(rslt);
loop.add_callback(wrapper);
return concurrent_future.result();
eventLoop = None;
application = tornado.web.Application([
(r'/data', WSHandler),
])
def startServer():
global eventLoop;
try:
print("Starting server #%s:%d" %("localhost",9090));
asyncio.set_event_loop(asyncio.new_event_loop());
eventLoop = tornado.ioloop.IOLoop();
application.listen(9090)
eventLoop.start();
except KeyboardInterrupt:
print("^C");
except:
print("ERR");
traceback.print_exc();
if __name__ == "__main__":
thread = Thread(target=startServer,);
thread.setDaemon(True);
thread.start();
time.sleep(5);
while True:
sendRandom();
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'm setting up a tornado web socket and want to use it to send log data over my network. Seems to work fine, but:
(1) Can I send data from client to server arbitrarily once the connection is established, or do I have to establish a new connection and use the on_open method to print/work with the sent message every time?
(2) In my client specifically (link): why isn't ws.close() called? Doesn't seem to happen. How can I terminate a connection then?
(3) Is there a better way to identify clients other that connection.request.remote_ip?
Code: Server
import tornado.ioloop
import tornado.web
import tornado.websocket
host = "localhost:2000"
class MainHandler(tornado.web.RequestHandler):
def get(self):
self.write("<h3>HELLO</h3>")
class EchoWebSocket(tornado.websocket.WebSocketHandler):
connections = set()
ips = set()
def open(self):
self.connections.add(self)
self.ips.add(self.request.remote_ip)
print("[MASTER]: WebSocket opened by {}".format(self.request.remote_ip))
def on_message(self, message):
print("[CLIENT {}]: {}".format(self.request.remote_ip, message))
[con.write_message("[MASTER]: {}".format(message)) for con in self.connections]
def on_close(self):
self.connections.remove(self)
self.ips.remove(self.request.remote_ip)
print("[MASTER]: WebSocket closed - Client {}".format(self.request.remote_ip))
def setup():
return tornado.web.Application([(r"/", MainHandler),
(r"/ws", EchoWebSocket)])
if __name__ == "__main__":
s = tornado.httpserver.HTTPServer(setup())
s.listen(2000)
print("------------- INFO -------------\nStarted Server at {}\nSocket available at /ws".format(host))
tornado.ioloop.IOLoop.current().start()
Client:
from tornado.websocket import websocket_connect
import tornado
import time
url = "ws://localhost:2000/ws"
class Client(object):
def __init__(self, url, log=None):
self.url = url
self.ioloop = tornado.ioloop.IOLoop.current()
self.conn = None
self.log = log
def start(self):
websocket_connect(
self.url,
self.ioloop,
callback=self.on_connected,
on_message_callback=self.on_message)
self.ioloop.start()
def on_connected(self, f):
try:
self.conn = f.result()
self.conn.write_message("Client #1 connected")
except Exception as e:
print("[ERROR]: {}".format(e))
self.conn.write_message("ERROR: {}".format(e))
self.ioloop.stop()
def on_message(self, message):
if message is None:
print("[ERROR]: No message received")
self.conn.write_message("[ERROR]: No message received")
self.ioloop.stop()
else:
print(message)
def close():
self.conn.close()
if __name__ == '__main__':
ws = Client(url)
ws.start()
ws.close()