Muhammad Faizan Khan
Muhammad Faizan Khan

Reputation: 10571

How to send server message to specific client in tornado websocket

Hey I am new to python and here is the Websocket server code in tornado

import tornado.ioloop
import tornado.web
import tornado.websocket
import tornado.template

class MainHandler(tornado.web.RequestHandler):
  def get(self):
    loader = tornado.template.Loader(".")
    self.write(loader.load("index.html").generate())

class WSHandler(tornado.websocket.WebSocketHandler):
  def open(self):
    print 'connection opened...'
    self.write_message("The server says: 'Hello'. Connection was accepted.")

  def on_message(self, message):
    self.write_message("The server says: " + message + " back at you")
    print 'received:', message

  def on_close(self):
    print 'connection closed...'

application = tornado.web.Application([
  (r'/ws', WSHandler),
  (r'/', MainHandler),
  (r"/(.*)", tornado.web.StaticFileHandler, {"path": "./resources"}),
])

if __name__ == "__main__":
  application.listen(9090)
  tornado.ioloop.IOLoop.instance().start()

It is working correctly and receiving my messages(client messages) on server but sadly it is not sending me other client messages. Like I have this html

<!DOCTYPE html>
<html>
<head>
  <title>WebSockets Client</title>  
  <script src="http://code.jquery.com/jquery-1.9.1.min.js"></script>
</head>
<body>
Enter text to send to the websocket server:
<div id="send">
    <input type="text" id="data" size="100"/><br>
    <input type="button" id="sendtext" value="send text"/>
</div>
<div id="output"></div>
</body>
</html>
<script>

jQuery(function($){

  if (!("WebSocket" in window)) {
    alert("Your browser does not support web sockets");
  }else{
    setup();
  }


  function setup(){

    // Note: You have to change the host var 
    // if your client runs on a different machine than the websocket server

    var host = "ws://localhost:9090/ws";
    var socket = new WebSocket(host);
    console.log("socket status: " + socket.readyState);   

    var $txt = $("#data");
    var $btnSend = $("#sendtext");

    $txt.focus();

    // event handlers for UI
    $btnSend.on('click',function(){
      var text = $txt.val();
      if(text == ""){
        return;
      }
      socket.send(text);
      $txt.val("");    
    });

    $txt.keypress(function(evt){
      if(evt.which == 13){
        $btnSend.click();
      }
    });

    // event handlers for websocket
    if(socket){

      socket.onopen = function(){
        //alert("connection opened....");
      }

      socket.onmessage = function(msg){
        showServerResponse(msg.data);
      }

      socket.onclose = function(){
        //alert("connection closed....");
        showServerResponse("The connection has been closed.");
      }

    }else{
      console.log("invalid socket");
    }

    function showServerResponse(txt){
      var p = document.createElement('p');
      p.innerHTML = txt;
      document.getElementById('output').appendChild(p); 
    }   


  }





});

</script>

When I am hit send button from client(using above html) it send my message to server but i want to send my message to other client. How do I send my message from server to other client like any desired client. The link given in comment provided me a way (make a global list variable, add every client in it and then loop and send message in message event) to send my message to all client but I also want my message to specific client.

Upvotes: 2

Views: 4445

Answers (1)

desertkun
desertkun

Reputation: 1037

You need some external system to do that. Like Ben Darnell said, other question explains some primitive ways to gather clients around.

What you need is to gather some kind of ID of each client upon initialization. That may be an account from your system, or you can generate new one for every new connection:

import uuid

clients = {}

class WSHandler(tornado.websocket.WebSocketHandler):
    def __init__(self, application, request, **kwargs):
        super(WSHandler, self).__init__(application, request, **kwargs)
        self.client_id = str(uuid.uuid4())

    def open(self):
        print 'connection for client {0} opened...'.format(self.client_id)
        clients[self.client_id] = self
        self.write_message("The server says: 'Hello'. Connection was accepted.")

    def on_message(self, message):
        self.write_message("The server says: " + message + " back at you")
        print 'received:', message

    def on_close(self):
        clients.pop(self.client_id, None)
        print 'connection closed...'

Later, you can use this completely made up client_id to tell other clients that a client with that id exists. Later you can send a message to him with

clients[<id>].write_message("Hello!")

As a side note, though, this approach will not scale well. In fact, you only would be able to address only clients who are connected to the current tornado instance only. If you need several instances, and a way to reach any client, see message brokers like rabbitmq.

Upvotes: 0

Related Questions