code: def create_sockets(self):
          
  
          ports = self.ports
  
          context = zmq.Context()
          poller = zmq.Poller()
  
          # Socket to handle init data
          rep = context.socket(zmq.REP)
          # this was inconsequent: here REQ is for the client, we reply with REP.
          # PULL and PUB is seen from here, not from the client.
          # Is now renamed to PUSH and SUB: everything is seen from outside.
          if "REQ" in ports:
              rep.bind(
                  "tcp://*:{port}".format(port=ports["REQ"])
              )
          else:
              ports["REQ"] = rep.bind_to_random_port(
                  "tcp://*"
              )
  
          pull = context.socket(zmq.PULL)
          if "PUSH" in ports:
              pull.bind(
                  "tcp://*:{port}".format(port=ports["PUSH"])
              )
          else:
              ports["PUSH"] = pull.bind_to_random_port(
                  "tcp://*"
              )
  
          # for sending model messages
          pub = context.socket(zmq.PUB)
          if "SUB" in ports:
              pub.bind(
                  "tcp://*:{port}".format(port=ports["SUB"])
              )
          else:
              ports["SUB"] = pub.bind_to_random_port(
                  "tcp://*"
              )
  
          poller.register(rep, zmq.POLLIN)
          poller.register(pull, zmq.POLLIN)
  
          sockets = dict(
              poller=poller,
              rep=rep,
              pull=pull,
              pub=pub
          )
          return sockets
docs: create zmq sockets