Combine Asyncio Tcp Server and Tornado Web Socket - python

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()
# [...]

Related

Tornado web socket communication on demand?

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()

python twisted - how to get client IP

How can i get the IP address of clients that are connecting to this dns server.
from twisted.internet import reactor
from twisted.names import client, dns, server
def main():
"""
Run the server.
"""
factory = server.DNSServerFactory(
clients=[client.Resolver(resolv='/etc/resolv.conf')]
)
protocol = dns.DNSDatagramProtocol(controller=factory)
reactor.listenUDP(10053, protocol)
reactor.listenTCP(10053, factory)
reactor.run()
if __name__ == '__main__':
raise SystemExit(main())
Thank you in advance :P
from twisted.internet import reactor
from twisted.names import client, dns, server
class PrintClientAddressDNSServerFactory(server.DNSServerFactory):
def buildProtocol(self, addr):
print("Connection to DNSServerFactory from {}".format(addr))
return server.DNSServerFactory.buildProtocol(self, addr)
class PrintClientAddressDNSDatagramProtocol(dns.DNSDatagramProtocol):
def datagramReceived(self, datagram, addr):
print("Datagram to DNSDatagramProtocol from {}".format(addr))
return dns.DNSDatagramProtocol.datagramReceived(self, datagram, addr)
def main():
"""
Run the server.
"""
factory = PrintClientAddressDNSServerFactory(
clients=[client.Resolver(resolv='/etc/resolv.conf')]
)
protocol = PrintClientAddressDNSDatagramProtocol(controller=factory)
reactor.listenUDP(10053, protocol)
reactor.listenTCP(10053, factory)
reactor.run()
if __name__ == '__main__':
raise SystemExit(main())

python tornado websocket server send message to specific client

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.

How can i send forever data to clients in tornado websocket?

I'm trying to run a server websocket using tornado, and i want to send messages to clients when i want in a loop not when i want and not onmessage.
This is my code for now :
import tornado.httpserver
import tornado.websocket
import tornado.ioloop
import tornado.web
class WSHandler(tornado.websocket.WebSocketHandler):
def open(self):
print 'new connection'
def on_message(self, message):
print 'message received: %s' % message
if message == "data":
self.write_message("message")
# here i want when i receive data from the client, to continue sending data for it until the connection is closed, and in the some time keep accepting other connections
def on_close(self):
print 'connection closed'
def check_origin(self, origin):
return True
application = tornado.web.Application([
(r'/ws', WSHandler),
])
if __name__ == "__main__":
http_server = tornado.httpserver.HTTPServer(application)
http_server.listen(8888)
myIP = socket.gethostbyname(socket.gethostname())
print '*** Websocket Server Started at %s***' % myIP
tornado.ioloop.IOLoop.instance().start()
Here's one way to do it, with a loop that sends a message every second until closed. The trickiest part of this is cancelling the loop when the connection is closed. This version uses the timeout parameter of Event.wait; other alternatives include gen.with_timeout and gen.sleep.
def open(self):
self.close_event = tornado.locks.Event()
IOLoop.current().spawn_callback(self.loop)
def on_close(self):
self.close_event.set()
#gen.coroutine
def loop(self):
while True:
if (yield self.close_event.wait(1.0)):
# yield event.wait returns true if the event has
# been set, or false if the timeout has been reached.
return
self.write_message("abc")

Fail on second call

I need your help please.
This code only works once, a second wget gives timeout (attached file).
wget http://localhost:9090
#!/usr/bin/env python
import trollius as asyncio
from trollius import From
import os
class Client(asyncio.Protocol):
def connection_made(self, transport):
self.connected = True
# save the transport
self.transport = transport
def data_received(self, data):
# forward data to the server
self.server_transport.write(data)
def connection_lost(self, *args):
self.connected = False
class Server(asyncio.Protocol):
clients = {}
def connection_made(self, transport):
# save the transport
self.transport = transport
#asyncio.coroutine
def send_data(self, data):
# get a client by its peername
peername, port = self.transport.get_extra_info('peername')
client = self.clients.get(peername)
# create a client if peername is not known or the client disconnect
if client is None or not client.connected:
protocol, client = yield From(loop.create_connection(Client, 'google.com', 80))
client.server_transport = self.transport
self.clients[peername] = client
# forward data to the client
client.transport.write(data)
def data_received(self, data):
# use a task so this is executed async
asyncio.Task(self.send_data(data))
#asyncio.coroutine
def initialize(loop):
# use a coroutine to use yield from and get the async result of
# create_server
server = yield From(loop.create_server(Server, '127.0.0.1', 9090))
loop = asyncio.get_event_loop()
# main task to initialize everything
asyncio.Task(initialize(loop))
# run
loop.run_forever()
Does anyone know the reason?
Thanks!
You need a real 'loop' in servers when you are writing socket servers in asyncio. Note that despite 'sync' coding, infinite loops do not block execution here. You need an infinite while loop within your server. There are many samples,I recommend websockets library samples!

Categories