Ressources informatiques

Ressources informatiques

Ressources informatiques

Créer un serveur UDP

Notre objectif est de créer un serveur UDP minimal : le serveur envoie la lettre suivante dans l'alphabet lorsque'il recoit une lettre. Il renvoie le caractère '!' s'il reçoit un caractère qui n'est pas une lettre ou s'il reçoit la lettre 'z' ou la lettre 'Z'.

Se documenter

Code du serveur

/**
 * @brief  Reçoit une lettre du client. Il envoie au client la lettre suivante dans l'alphabet.
 * Il envoie le caractère '!' s'il reçoit un caractère qui n'est pas une lettre ou s'il reçoit la lettre 'z' ou la lettre 'Z'.
 */

#include <string.h>     // pour memset()
#include <sys/types.h>  // pour socket(), setsockopt(), bind(), recvfrom(), sendto()
#include <sys/socket.h> // pour socket(), setsockopt(), bind(), recvfrom(), sendto()
#include <stdio.h>      // pour fprintf(), perror()
#include <arpa/inet.h>  // pour htonl(), htons()

// Numéro de port du serveur
#define NUMERO_PORT_SERVEUR 40000

int main() {
    struct sockaddr_in coupleIPPortServeur;
    struct sockaddr_in coupleIPPortClient;
    socklen_t longueurClient;
    int maSocket;
    int optval;
    char lettre;
    char lettreReponse;

    // Initialiser les structures a des octets de valeurs 0
    memset(&coupleIPPortServeur, 0, sizeof(struct sockaddr_in));

    // Creer la socket serveur en mode UDP
    if ((maSocket = socket(AF_INET, SOCK_DGRAM, 0)) == -1)
    {perror("socket");  return -1;}

    // Reutiliser le meme port en cas d'interruption brutal du serveur
    optval = 1;
    setsockopt(maSocket, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof optval);

    // Ouvrir un port
    coupleIPPortServeur.sin_family = AF_INET;
    coupleIPPortServeur.sin_addr.s_addr = htonl(INADDR_ANY);
    coupleIPPortServeur.sin_port = htons(NUMERO_PORT_SERVEUR);
    if (bind(maSocket, (struct sockaddr *)(&coupleIPPortServeur), sizeof(struct sockaddr_in)) == -1)
    {perror("bind"); return -1;}

    while (1) {
        lettreReponse = '!';
        longueurClient = sizeof(coupleIPPortClient);

        // Recevoir la requête
        if (recvfrom(maSocket, &lettre, sizeof(lettre), 0, (struct sockaddr *)(&coupleIPPortClient), &longueurClient) == -1)
        {perror("recvfrom"); return -1; }

        // Traiter la requete
        if ((lettre >= 'a' && lettre < 'z') || (lettre >= 'A' && lettre < 'Z'))
            lettreReponse = lettre + 1;

        // Envoyer la réponse
        if (sendto(maSocket, &lettreReponse, sizeof(lettreReponse), 0, (struct sockaddr *)(&coupleIPPortClient), longueurClient) == -1)
        {perror("sendto"); return -1;}
    }

    return 0;
}

Tester

Vérifier que le port 40000 de notre serveur UDP est ouvert

Commande ss : another utility to investigate sockets
Options :

doe@debian:~$ ss -lnup | grep serveur
UNCONN    0         0                  0.0.0.0:40000             0.0.0.0:*       users:(("serveurUdp",pid=1222,fd=3))

Valider le protocole applicatif

J'utilise, pour valider le serveur, le client UDP correspondant.

doe@debian:~$ ./clientUdp 
usage : clientUdp <adresse_IP_serveur> <numero_de_port_serveur> <lettre>
exemple : clientUdp 192.168.1.11 40000 a
doe@debian:~$ ./clientUdp 192.168.1.101 30000 d
recvfrom: Resource temporarily unavailable
communiquerAvecLeServeur : echec
doe@debian:~$ ./clientUdp 192.168.1.101 40000 d
J'ai émis : d, j'ai reçu : e
doe@debian:~$ ./clientUdp 192.168.1.101 40000 D
J'ai émis : D, j'ai reçu : E
doe@debian:~$ ./clientUdp 192.168.1.101 40000 z
J'ai émis : z, j'ai reçu : !
doe@debian:~$ ./clientUdp 192.168.1.101 40000 Z
J'ai émis : Z, j'ai reçu : !
doe@debian:~$ ./clientUdp 192.168.1.101 40000 ?
J'ai émis : ?, j'ai reçu : !