예제 #1
0
 def __init__(self, timeout, missed_ping, simulation):
   self.__client_lock = threading.Lock()
   self.__timed_out_lock = threading.Lock()
   self.__clients = {}
   self.__clientids = {}
   self.__timed_out = []
   self.__ping_pong = {}
   self.__t = PeriodicTimer(timeout, self.__timeout_check, [self])
   ## The number of missed pings allowed
   self.missed_ping = missed_ping
   self.__sim = simulation
   self.__t.daemon = True
예제 #2
0
class ClientManager():
  ## Class constructor
  # @param self The instance of playernsd::ClientManager.
  # @param timeout The number of seconds for a timeout.
  # @param missed_ping The number of missed pings allowed before a disconnect.
  def __init__(self, timeout, missed_ping, simulation):
    self.__client_lock = threading.Lock()
    self.__timed_out_lock = threading.Lock()
    self.__clients = {}
    self.__clientids = {}
    self.__timed_out = []
    self.__ping_pong = {}
    self.__t = PeriodicTimer(timeout, self.__timeout_check, [self])
    ## The number of missed pings allowed
    self.missed_ping = missed_ping
    self.__sim = simulation
    self.__t.daemon = True
  ## Start the timeout poller
  # @param self The instance of playernsd::ClientManager.
  def start(self):
    self.__t.start()
  ## Stop the timeout poller
  # @param self The instance of playernsd::ClientManager.
  def stop(self):
    self.__t.cancel()
  ## Add a client that should be polled by the timeout poller
  # @param self The instance of playernsd::ClientManager.
  # @param address The address of the client.
  # @param client The associated playernsd:RemoteClient object.
  def add_client(self, address, client):
    with self.__client_lock:
      self.__clients[address] = RemoteClient(None, address, None, client)
      self.__ping_pong[address] = 1
  ## Register a client with name and protocol version.
  # @param name The name of the client.
  # @param address The addres sof the client.
  def register_client(self, address, name, version):
    self.__clients[address].name = name
    self.__clients[address].version = version
    self.__clientids[name] = self.__clients[address]
    if self.__sim:
      self.__sim.new_client(name)
  ## Check if the address is handled by the client manager.
  # @param identifier The identifier to refer uniquely to a client.
  def has_client(self, identifier):
    if isinstance(identifier, str):
      return identifier in self.__clientids
    else:
      return identifier in self.__clients
  ## Check if the address is already registered in the client manager.
  # @param identifier The identifier to refer uniquely to a client.
  def is_registered(self, identifier):
    if isinstance(identifier, str):
      return identifier in self.__clientids
    else:
      return identifier in self.__clients and self.__clients[identifier].name != None
  ## Get a RemoteClient object when identified by address or id.
  # @param identifier The identifier to refer uniquely to a client.
  def get_client(self, identifier):
    if isinstance(identifier, str):
      return self.__clientids[identifier]
    else:
      return self.__clients[identifier]
  ## Remove a client that is polled by the timeout poller.
  # @param self The instance of playernsd::ClientManager.
  # @param address The address of the client.
  def remove_client(self, address):
    with self.__client_lock:
      if self.__clients[address].name in self.__clientids:
        if self.__sim:
          self.__sim.remove_client(self.__clients[address].name)
        del self.__clientids[self.__clients[address].name]
      del self.__clients[address]
  ## Get a list of client ids.
  def get_clientid_list(self):
    l = []
    for v in self.__clients.itervalues():
      l.append(v.name)
    return l
  ## Indicate that a particular client has replied to a ping.
  # @param self The instance of playernsd::ClientManager.
  # @param address The address of the client.
  def pong(self, address):
    self.__ping_pong[address] = 0
  ## Check if a particular client has timed out.
  # @param self The instance of playernsd::ClientManager.
  # @param address The address of the client.
  # @return Boolean return indicating whether the client has timed out.
  def is_timed_out(self, address):
    return address in self.__timed_out or \
      self.__ping_pong[address] < -self.missed_ping
  ## Send a message to a client.
  #
  # This is a wrapper function to send a message to a client.
  # By default, this sends the message to the client that invoked the request,
  # but with the parameters @a s (the target socket) and
  # @a ca (the target client address), any client can be messaged.
  # @param self The playernsd::ClientManager instance.
  # @param msg The message to be sent.
  # @param s The socket to send the message to.
  # @param ca The client address to send the message to.
  def send(self, msg, s, ca):
    if VERBOSE > 1:
      self.log(ca, 'SEND(' + str(len(msg)) + ')', msg)
    # read the type of message, and see if the message should be
    # simulated
    command = msg.split(' ')
    if simulation and (command[0] == 'msgtext' or command[0] == 'msgbin'):
      self.__sim.send(command[1], self.__clientids(ca),
        msg[msg.find('\n')+1:])
    else:
      s.send(msg)
  ## Get a property from the simulation
  #
  # This function requests a value from the simulation.
  # @param self The playernsd::ClientManager instance.
  # @param ca The client address that this request comes from.
  # @param prop The name of the property.
  def prop_get_sim(self, prop, ca):
    if self.__sim:
      cid = self.__clients[ca].name
      self.__sim.prop_get(cid, prop)
    else:
      self.__clients[ca].socket.send('propval ' + prop + ' ' + '\n')
  ## Set a property in the simulation
  #
  # This function sets a value from the simulation.
  # @param self The playernsd::ClientManager instance.
  # @param _from The client address that this request comes from.
  # @param prop The name of the property.
  # @param val The value of the property.
  def prop_set_sim(self, prop, val, ca):
    if self.__sim:
      cid = self.__clients[ca].name
      self.__sim.prop_set(cid, prop, val)
  ## Broadcast a message to all clients.
  #
  # This is a wrapper function to broadcast a message to all clients.
  # @param self The playernsd::ClientManager instance.
  # @param msg The message to be sent to all clients.
  def broadcast(self, msg):
    if simulation:
      command = msg.split(' ')
      self.__sim.send(command[1], '__broadcast__',
        msg[msg.find('\n')+1:])
    else:
      for v in self.__clients.itervalues():
        if msg.split(' ')[1] != v.name:
          self.send(msg, v.socket, v.address)
  ## Receive a message from a client.
  #
  # This is a wrapper function to receive a message from a client and
  # logs it.
  # @param self The playernsd::ClientManager instance.
  # @param s The socket to send the message to.
  # @param ca The client address to send the message to.
  # @return The message received from the client.
  def recv(self, s, ca):
    data = s.recv(MAX_READ)
    if VERBOSE > 1:
      self.log(ca, 'RECV(' + str(len(data)) + ')', data)
    return data
  ## Receive a message from the simulation.
  def recv_sim(self, _from, to, msg):
    self.__clientids[to].socket.send('msgbin ' + _from + ' ' + str(len(msg)) + '\n' + msg)
  ## Receive a property value from the simulation.
  def prop_val_sim(self, _from, prop, val):
    #if val == "":
      #self.send('error propnotexist\n') # TODO: Handle empty strings separately?
    #else:
    self.__clientids[_from].socket.send('propval ' + prop + ' ' + str(val) + '\n')
  ## Create a log message.
  #
  # This is used internally to log sent and received messages.
  # This is typically only called when --verbose is passed to the daemon.
  # @param self The playernsd::ClientManager instance.
  # @param ca Client address related to log message.
  # @param tag Tag indicating the log level.
  # @param msg The message to be logged.
  def log(self, ca, tag, msg):
    if len(msg):
      logmsg = tag + ': ' + msg.encode(sys.stdout.encoding,
        'backslashreplace').replace('\n', '\\n')
      log.debug(self.get_id(ca) + ' ' + logmsg)
  ## Get id of client or else return '__unregistered'
  def get_id(self, ca):
    if ca in self.__clients and self.__clients[ca].name != None:
      return '[' + str(ca) + ', ' + self.__clients[ca].name + ']'
    else:
      return '[' + str(ca) + ', __unregistered]'
  # Callback function that periodically checks all clients to see whether
  # they are still responding.
  # @param args Additional arguments.
  # @param args Additional keyword arguments.
  def __timeout_check(self, args, kwargs):
    with self.__client_lock:
      for k,v in self.__clients.iteritems():
        try:
          self.send('ping\n', v.socket, k)
          self.__ping_pong[k] -= 1
          if self.__ping_pong[k] < -self.missed_ping:
            log.warn(str(k) + ' has missed at least ' +
              str(-self.__ping_pong[k]+1) + ' pings, closing connection')
            self.send('error missedping\n', v.socket, k)
            v.socket.shutdown(1)
        except socket.error, msg:
          if not self.is_timed_out(k):
            self.__timed_out.append(k)
            log.warn('Lost connection to ' + str(k) + ' ' + str(msg))