wiki:IOC_T06

Version 2 (modified by franck, 6 years ago) (diff)

--

Modèle client-serveur et MQTT

Objectif de la séance

Le but de cette séance est d'expérimenter le modèle client-serveur et une évolution de ce modèle client-broker pour permettre l'échange d'informations entre les capteurs et la base au dessus du protocole WiFi?. MQTT est un protocole construit au dessus de TCP/IP. Dans un premier temps, vous allez commencer par mettre en œuvre le modèle clients-serveur en créant votre propre application sur ce modèle et, dans un second temps, vous utilisez MQTT.

Mise en œuvre du modèle client-serveur sur une application de vote

L’apprentissage du modèle client-serveur va se faire en étant guidé par un exemple que vous allez devoir comprendre et à partir duquel vous allez devoir créer une nouvelle application. Il y a donc deux temps :

  1. Vous devez commenter l’exemple dans le code en vous aidant de la documentation de la bibliothèque SDL. Le but de ces commentaires est de vous forcer à lire la documentation pour les quelques fonctions présentes dans l’exemple.
  2. `Vous allez créer une nouvelle application permettant de recueillir le vote de personne concernant le choix de l’heure d’hiver ou d’été. Vous avez deux programmes à écrire. Le premier permet de voter, c’est le client. Le second permet de recueillir le vote, c’est le serveur
  • L'application "client"
    • Paramètres :
      • L’adresse IP du serveur
      • Le numéro de port de l’application serveur
      • Le nom de la personne
      • Le vote avec deux possibilités : été ou hiver
    • Comportement attendu :
      • Le client crée un socket, se connecte et envoi son vote au serveur.
      • Le serveur lui renvoie un acquittement de son vote.
  • L'application "serveur"
    • Paramètres :
      • Le numéro du port d’écoute.
    • Comportement attendu :
      • Le serveur un socket et écoute
      • Lorsqu’un client se connecte, il vérifie que le client n’a pas encore voté
        • Si ok, le serveur envoie le message « a voté »
        • Si ko, le serveur envoie le message « erreur, vote déjà réalisé »

Schéma de principe d'un échange client-serveur avec le protocole TCP (connecté)

client-serveur.png

Illustration dans un programme où le client envoie un message à un serveur (qui ne lui répond pas).

client.c :

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>

void error(const char *msg)
{
        perror(msg);
        exit(0);
}

int main(int argc, char *argv[])
{
        int sockfd, portno, n;
        struct sockaddr_in serv_addr;
        struct hostent *server;

        char buffer[256];

        // Le client doit connaitre l'adresse IP du serveur, et son numero de port
        if (argc < 3) {
                fprintf(stderr,"usage %s hostname port\n", argv[0]);
                exit(0);
        }
        portno = atoi(argv[2]);
        
        // 1) Création de la socket, INTERNET et TCP

        sockfd = socket(AF_INET, SOCK_STREAM, 0);
        if (sockfd < 0)
                error("ERROR opening socket");

        server = gethostbyname(argv[1]);
        if (server == NULL) {
                fprintf(stderr,"ERROR, no such host\n");
                exit(0);
        }

        // On donne toutes les infos sur le serveur

        bzero((char *) &serv_addr, sizeof(serv_addr));
        serv_addr.sin_family = AF_INET;
        bcopy((char *)server->h_addr, (char *)&serv_addr.sin_addr.s_addr, server->h_length);
        serv_addr.sin_port = htons(portno);

        // On se connecte. L'OS local nous trouve un numéro de port, grâce auquel le serveur
        // peut nous renvoyer des réponses, le \n permet de garantir que le message ne reste
        // pas en instance dans un buffer d'emission chez l'emetteur (ici c'est le clent).

        if (connect(sockfd,(struct sockaddr *) &serv_addr,sizeof(serv_addr)) < 0)
                error("ERROR connecting");

        strcpy(buffer,"Coucou Peri\n");
        n = write(sockfd,buffer,strlen(buffer));

        // On ferme la socket

        close(sockfd);
        return 0;
}

server.c

#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>

#include <netdb.h>
#include <arpa/inet.h>


void error(const char *msg)
{
        perror(msg);
        exit(1);
}

int main(int argc, char *argv[])
{
        int sockfd, newsockfd, portno;
        socklen_t clilen;
        char buffer[256];
        struct sockaddr_in serv_addr, cli_addr;
        int n;

        if (argc < 2) {
                fprintf(stderr, "ERROR, no port provided\n");
                exit(1);
        }

        // 1) on crée la socket, SOCK_STREAM signifie TCP

        sockfd = socket(AF_INET, SOCK_STREAM, 0);
        if (sockfd < 0)
                error("ERROR opening socket");

        // 2) on réclame au noyau l'utilisation du port passé en paramètre 
        // INADDR_ANY dit que la socket va être affectée à toutes les interfaces locales

        bzero((char *) &serv_addr, sizeof(serv_addr));
        portno = atoi(argv[1]);
        serv_addr.sin_family = AF_INET;
        serv_addr.sin_addr.s_addr = INADDR_ANY;
        serv_addr.sin_port = htons(portno);
        if (bind(sockfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0)
                error("ERROR on binding");


        // On commence à écouter sur la socket. Le 5 est le nombre max
        // de connexions pendantes

        listen(sockfd, 5);
        while (1) {
                newsockfd = accept(sockfd, (struct sockaddr *) &cli_addr, &clilen);
                if (newsockfd < 0)
                    error("ERROR on accept");

                bzero(buffer, 256);
                n = read(newsockfd, buffer, 255);
                if (n < 0)
                    error("ERROR reading from socket");

                printf("Received packet from %s:%d\nData: [%s]\n\n",
                       inet_ntoa(cli_addr.sin_addr), ntohs(cli_addr.sin_port),
                       buffer);

                close(newsockfd);
        }

        close(sockfd);
        return 0;
}