Is it possible to send a message to all active Web

2019-06-16 21:43发布

问题:

I'm experimenting with building a websocket's based application.

I am wondering whether it's possible to send a message to all active connections as they are persistant.

Say I'm running a live auction site and I have multiple users watching the auction page, each of them is connected via sockets to my server. Now let's say one user raises the bid. I want to send a message to all connected clients. Easiest way is to have the clients poll the server via socket every second, but I think the idea of websockets is to have true bi-directional communication.

How can this be done?

thanks in advance,

Rotem

回答1:

socket.io solution:

// note, io.listen() will create a http server for you
var io = require('socket.io').listen(80);

io.sockets.on('connection', function (socket) {
  io.sockets.emit('this', { will: 'be received by everyone' });

  socket.on('private message', function (msg) {
    console.log('I received a private message from ', socket.id, ' saying ', msg);
    // Echo private message only to the client who sent it
    socket.emit('private message', msg);
  });

  socket.on('disconnect', function () {
    // This will be received by all connected clients
    io.sockets.emit('user disconnected');
  });
});

all_active_connections = {};

webocket server (there are many), do same manually:

  var ws = require("ws");

  global_counter = 0;
  all_active_connections = {};

  ws.createServer(function (websocket) 
  {
      websocket.on('connect', function() 
      {
          var id = global_counter++;
          all_active_connections[id] = websocket;
          websocket.id = id; 
      }).on('data', function (data) {
          if (data == 'broadcast me!')
          {
              for (conn in all_active_connections)
                 all_active_connections[conn].write(data);
          }       
      }
    }).on('close', function() {
        delete all_active_connections[websocket.id];
    });
  }).listen(8080);


回答2:

For a solution based on tornado/tornadio, you SocketConnection class needs to maintain a list of connections at the class level. Your on_connect handler would add the connection to this list, and on_close would remove it. For sample pseudo-code see this post by Serge S. Koval. The code is reproduce below:

Declare your TornadIO connection class:

class MyConnection(SocketConnection):
    participants = set()

    @classmethod
    def broadcast(cls, msg):
        for p in cls.participants:
            p.send(msg)

    @classmethod
    def controller_msg(cls, msg):
        cls.broadcast(msg)

In your device polling thread, do something like:

while True: 
    datum = file.readline() 
    if len(datum) > 2: 
        t = json.loads(datum) 
        ...
        def callback():
            MyConnection.controller_msg(t)

        io_loop.add_callback(callback)

Additionally, gevent-socketio has support for message broadcast, but it's based on gevent, not tornado.

UPDATE:

tornadio2 already maintains a list of active sessions, so all you need to do is:

class MyConnection(SocketConnection):
    def broadcast(self, event, message):
        for session_id, session in self.session.server._sessions._items.iteritems():
            session.conn.emit(event, message)

This works because each connection instance has a reference to its session, which has a reference to the global router used to create the application (stored as server), which maintains a list of sessions in a SessionContainer object in _sessions. Now whenever you want to broadcast a message within your connection class, just do:

self.broadcast('my_custom_event', 'my_event_args')


回答3:

This redis + websockets (on tornado) example should help you. Basically you have a list of listeners who should be notified and as soon as a message is received iterate through that list and inform them.