pythonsocketshandlersocketservertcpsocket

Creating multiple tcp socket handlers from one class in Python


Is it possible in Python3 to create a TCP Socket handler class that can have multiple unique instances?

I have 2 threaded TCP Socket servers that are providing unique information to the connected client. I am trying to make it so that 'node 1' socket server handler will provide only the data for 'node 1' while 'node 2' socket server handler will provide on the data for 'node 2'.

I have made a custom handler with the variable 'node'.

class NodeTCPHandler(socketserver.BaseRequestHandler):

    def __init__(self, request, client_address, server):
        socketserver.BaseRequestHandler.__init__(self, request, client_address, server)
        self.node = 6
        return
    
    def handle(self):
        logging.debug("Node Socket has been started")
        global stop, temperature, humidity, pressure, voltage
        
        logging.debug("{} connected to Node 0{} Socket".format(self.client_address[0], self.node+1))
        
        while not stop:
            msg = (str(temperature[self.node])+','+str(humidity[self.node])+','+str(pressure[self.node])+','+str(voltage[self.node]))
            self.request.sendall(msg.encode())
            time.sleep(5)

I have tried to set the variable 'node' after creating the socket server and defining the handler.

node_01_server = socketserver.TCPServer((hostAddr, nodePort[0]), NodeTCPHandler)
node_01_server.RequestHandlerClass.node = 0
node_02_server = socketserver.TCPServer((hostAddr, nodePort[1]), NodeTCPHandler)
node_02_server.RequestHandlerClass.node = 1

node_01_thread = threading.Thread(name='Node 01 Socket',target=node_01_server.serve_forever)
node_02_thread = threading.Thread(name='Node 02 Socket',target=node_02_server.serve_forever)

This is close to working, but when I set the 'node_02_server.RequestHandlerClass.node' it over writes the 'node_01_server.RequestHandlerClass.node' node variable as well!

I am obviously missing something but I feel I am close to a solution. This is my first real attempt at using classes and socket servers so if I have made any obvious mistakes please let me know.


Solution

  • Line node_01_server.RequestHandlerClass.node = 0 sets class attribute RequestHandlerClass.node, not instance attribute which it seems you would like to change. And since there can be only one class attribute node of the class RequestHandlerClass it is updated on each such line.

    Let's consider a couple of ways to separate your handlers:

    1. Implement several NodeTCPHandler and pass the right one into TCPServer

      class BaseNodeTCPHandler(socketserver.BaseRequestHandler):
          NODE_TYPE = 6
      
          def __init__(self, *args, **kwargs):
              self.node = self.NODE_TYPE
              super().__init__(*args, **kwargs)
      
      class Node0TCPHandler(BaseNodeTCPHandler):
          NODE_TYPE = 0
      
      class Node1TCPHandler(BaseNodeTCPHandler):
          NODE_TYPE = 1
      

      and now you can pass those Node0TCPHandler/Node1TCPHandler into an appropriate server

      node_01_server = socketserver.TCPServer((hostAddr, nodePort[0]), Node0TCPHandler)
      node_02_server = socketserver.TCPServer((hostAddr, nodePort[1]), Node1TCPHandler)
      
    2. Implement custom TCPServer which can take additional arguments and pass them into the handler's constructor

      class NodeTCPHandler(socketserver.BaseRequestHandler):
      
          def __init__(self, node, *args, **kwargs):
              self.node = node
              super().__init__(*args, **kwargs)
      
      class CustomTCPServer(socketserver.TCPServer):
      
          def __init__(self, *args, node=6, **kwargs):
              super().__init__(*args, **kwargs)
              self.node = node
      
          def finish_request(self, request, client_address):
              """Finish one request by instantiating RequestHandlerClass."""
              self.RequestHandlerClass(self.node, request, client_address, self)
      
      

      and now you can instantiate servers with different node values like that:

      node_01_server = CustomTCPServer((hostAddr, nodePort[0]), NodeTCPHandler, node=0)
      node_02_server = CustomTCPServer((hostAddr, nodePort[1]), NodeTCPHandler, node=1)
      

    The first approach is better since it requires fixes in a smaller number of classes, especially if more similar updates are coming.