dernière modification : 2024

Application serveur

Une application serveur est une application offrant des services à d’autres applications (locales ou distantes) appelées applications clientes.

1. Aperçu

L’application serveur est un programme à l’écoute de requêtes provenant de clients connectés au réseau. Cette application choisit sur quelle IP et quel port elle écoute les requêtes.

Adresses IP d’écoute

Un serveur peut écouter via n’importe laquelle de ses adresses, dont en particulier :

  • 127.0.0.1 accessible uniquement en local : s’il écoute seulement sur 127.0.0.1 (localhost), alors il ne pourra être sollicité que par des clients locaux (résidant sur la même machine)

  • 0.0.0.0 qui représentant toutes ses adresses (dont 127.0.0.1).

Port d’écoute

Le port est un entier identifiant le service. Les ports <1024 sont normalisés et réservés à des services internet bien connus (well-known ports). Ils sont définis dans le fichier /etc/services. Une application sans droit particulier ne peut pas écouter sur un port <1024. C’est pourquoi ces ports sont appelés ports privilégiés.

Le couple (IP,port) s’appelle un network socket. C’est un point d’entrée de service unique sur le réseau.

Un serveur digne de ce nom ne doit pas faire attendre ses clients, en étant par exemple monopolisé par un client accaparant.

2. Problème

On dispose d’un embryon d’application client/serveur :

le serveur
#!/bin/env python3

from socket import *

host = "localhost"
port = 2074
buf = 1024                            # taille du buffer
s_addr = (host,port)

UDPsock = socket(AF_INET,SOCK_DGRAM)  # création du socket
UDPsock.bind(s_addr)                  # activation

while True:
    data,c_addr = UDPsock.recvfrom(buf)  # écoute
    print(f"\nReçu {data} de {c_addr}")

UDPsock.close()

et

le client
#!/bin/env python3

from socket import *

host = "localhost"
port = 2074
buf = 1024                              # taille du buffer
s_addr = (host,port)

UDPsock = socket(AF_INET,SOCK_DGRAM)    # création du socket

while True:
    msg = input('>> ')
    if not msg: break

    data = bytes(msg,'utf-8')
    print(f"Envoi de {data}")
    UDPsock.sendto(data,s_addr)         # envoi vers le serveur

UDPsock.close()

Question 1

Comprendre le fonctionnement de ces 2 programmes, et les tester en local.

Question 2

Paramétrer le client de façon à pouvoir spécifier au lancement (en argument de la ligne de commande) le serveur à atteindre. Tester ensuite le cas où le client et le serveur ne sont pas sur la même machine.

Question 3

On souhaite maintenant observer le comportement du serveur lorsque le traitement des requêtes prend du temps. On définit pour cela le protocole suivant :

  • si le serveur reçoit une requete dont le format est :

    <entier> "<chaine 1>" "<chaine 2>"

    alors le serveur écrit sur son écran :

    "<chaine1>" envoyé par <adresse du client>

    puis attend entier secondes avant d’écrire :

    "<chaine2>" envoyé par <adresse du client> il y a <entier> secondes
  • si la requête n’a pas le format précédent, le serveur écrit sur son écran :

    requête incompréhensible de <adresse du client>

Exemple, le client envoie

10 "debut" "fin"

au serveur qui doit afficher :

"debut" envoyé par 10.0.0.1

puis attendre 10 secondes, puis afficher :

"fin" envoyé par 10.0.0.1 il y a 10 secondes

Autre exemple, le client envoie :

10 debut fin

au serveur qui doit afficher :

requête incompréhensible de 10.0.0.1

Question 4

On souhaite maintenant que le serveur ne fasse pas attendre inutilement ses clients. A l’aide du module Python threading, réaliser un serveur qui traite équitablement ses clients.