0

I'm using a SocketServer.ThreadingTCPServer to serve socket connections to clients. This provides an interface where users can connect, type commands and get responses. That part I have working well.

However, in some cases I need a separate thread to broadcast a message to all connected clients. I can't figure out how to do this because there is no way to pass arguments to the class instantiated by ThreadingTCPServer. I don't know how to gather a list of socket connections that have been created.

Consider the example here. How could I access the socket created in the MyTCPHandler class from the __main__ thread?

Dave
  • 1,326
  • 2
  • 11
  • 22

2 Answers2

1

You should not write to the same TCP socket from multiple threads. The writes may be interleaved if you do ("Hello" and "World" may become "HelWloorld").

That being said, you can create a global list to contain references to all the server objects (who would register themselves in __init__()). The question is, what to do with this list? One idea would be to use a queue or pipe to send the broadcast data to each server object, and have the server objects look in that queue for the "extra" broadcast data to send each time their handle() method is invoked.

Alternatively, you could use the Twisted networking library, which is more flexible and will let you avoid threading altogether - usually a superior alternative.

John Zwinck
  • 239,568
  • 38
  • 324
  • 436
  • Thanks. That is roughly the road I have gone down. Twisted isn't a good option. I'm running on an embedded device and the twisted module takes 10 seconds to load for some reason, which is a bug I haven't been able to figure out yet. The solution I nearly have working is to instead of using a global, accessing the "server" object, which all threads have access to. So when a new socket is created, it adds it to a dict, and deletes it from the dict when the socket closes. – Dave May 24 '16 at 13:34
1

Here is what I've come up with. It isn't thread safe yet, but that shouldn't be a hard fix:

When the socket is accepted:

    if not hasattr(self.server, 'socketlist'):
        self.server.socketlist = dict()

    thread_id = threading.current_thread().ident
    self.server.socketlist[thread_id] = self.request

When the socket closes:

del self.server.socketlist[thread_id]

When I want to write to all sockets:

def broadcast(self, message):
    if hasattr(self._server, 'socketlist'):
        for socket in self._server.socketlist.values():
            socket.sendall(message + "\r\n")

It seems to be working well and isn't as messy as I thought it might end up being.

Dave
  • 1,326
  • 2
  • 11
  • 22