#!/usr/bin/python3
# coding=utf-8

import easysocket
import rpc
import sys
import socket
import threading

# Gestion de la ligne de commande.
# ##################################################################

# Le port est fourni dans la ligne de commande.
if len (sys.argv) != 2:
      print ('One mandatory parameter: SERVERPORT')
      sys.exit(2)
server_port = int (sys.argv [1])


# Ouverture d'un socket.
# ##################################################################

# Il faut ouvrir un socket serveur.
server_socket = easysocket.make_server_socket (server_port)


# Fonctions exportées.
# ##################################################################

# Ces fonctions seront appelées à distance : les définitions des fonctions sont
# ici, dans ce module, non visibles aux clients.
#
# Définissons donc quelques fonctions pour les clients ...
def plus (a, b):
    return a + b
def fact (n):
    if n == 0:
        return 1
    else:
        return n * fact (n - 1)
def foobar ():
    raise MemoryError ()  # Je soulève une exception, quelle qu'elle soit.

# ... et exportons-les.  socket.gethostname est prédéfinie et non définie ici.
# Rien ne change : elle est une fonction, comme les autres.
rpc.export_rp ('plus', plus)
rpc.export_rp ('fact', fact)
rpc.export_rp ('foobar', foobar)
rpc.export_rp ('hostname', socket.gethostname)

# Les fonctions que j'ai exporté seront donc disponible pour être appelées à
# distance, par les clients.

# Le reste de ce fichier est l'implémentation d'un serveur, très ordinaire.


# Service des requêtes.
# ##################################################################

# On sert chaque client dans un thread différent.
class Serve_one_client_thread (threading.Thread):
      # On stocke le socket vers le client dans une variable d'instance
      # de cette sous-classe de Thread.
      def __init__ (self, socket_to_client):
            threading.Thread.__init__ (self)
            self.socket_to_client = socket_to_client

      # Le vrai travail consiste juste à appeler rpc.serve_requests.
      def run (self):
            try:
                  rpc.serve_requests (self.socket_to_client)
            except:
                  print ('Error serving a client.  Exiting.')
            # On est sorti, à cause d'une exception.  Fermons le socket.
            try:
                  socket_to_client.shutdown (socket.SHUT_RDWR)
                  socket_to_client.close ()
            except:
                  # Si la fermeture du socket échoue, ce n'est pas un
                  # problème : je ne fais rien.
                  pass

# Dans une boucle infinie, servons les clients.
while True:
    # Attend la prochaine connexion ...
    socket_to_client, addr = server_socket.accept ()
    # ... La connexion est arrivée.  J'ai un nouveau client, à servir dans un
    # nouveau thread que je démarre immédiatement.
    thread = Serve_one_client_thread (socket_to_client)
    thread.start ()
