IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Réseau C Discussion :

Exercice client/serveur sur Linux : "read: Bad file descriptor"


Sujet :

Réseau C

  1. #1
    Nouveau Candidat au Club
    Homme Profil pro
    En reconversion
    Inscrit en
    Septembre 2022
    Messages
    2
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Pyrénées Orientales (Languedoc Roussillon)

    Informations professionnelles :
    Activité : En reconversion

    Informations forums :
    Inscription : Septembre 2022
    Messages : 2
    Points : 1
    Points
    1
    Par défaut Exercice client/serveur sur Linux : "read: Bad file descriptor"
    Bonjour à tous,

    Actuellement en train d'essayer de comprendre les sockets via le langage C sous Linux ( échange des données à travers une connexion socket), j'ai d'abord créé un fichier serveur.c qui prend en argument le numéro de port d’écoute et, en option, l’adresse IPv4 d’écoute et un fichier client.c qui prend en argument le numéro de port et l’adresse IPv4 du serveur.

    Pour l'instant, lpour essayer de mieux comprendre, le serveur ne sert qu’un seul client et se termine avec la fin de la connexion. Le client envoie toutes les trente secondes au serveur une demande d’état. Le serveur répond en
    fournissant son PID. Le client se termine sur réception d’un signal.

    Seulement, quand je lance en premier le serveur, j'ai bien ce dernier qui est à l'écoute :
    Nom : image_1.png
Affichages : 318
Taille : 4,9 Ko
    Je lance ensuite le client :
    Nom : image_2.png
Affichages : 324
Taille : 8,9 Ko
    Mais immédiatement côté serveur, la connexion se ferme et j'ai le message "read: Bad file descriptor"
    Nom : image_3.png
Affichages : 321
Taille : 9,2 Ko

    Pourriez-vous me dire où ça pêche dans le code car je ne vois pas ?
    D'avance merci.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    #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 <signal.h>
    #include <arpa/inet.h>
    #include <time.h> // Ajouter cette ligne pour utiliser la fonction time()
     
     
    // Fonction pour la création d'un socket client
    int creer_socket_client(const char* adresse_ip, int port) {
        int socket_client; // Numéro de la socket client
        struct sockaddr_in adresse_serveur; // Adresse du serveur
     
        // Création de la socket
        socket_client = socket(AF_INET, SOCK_STREAM, 0);
        if (socket_client == -1) {
            perror("socket client");
            exit(-1);
        }
     
        // Configuration de la socket
        adresse_serveur.sin_family = AF_INET; // Protocole de la socket (IP)
        adresse_serveur.sin_port = htons(port); // Numéro de port du serveur
        inet_aton(adresse_ip, &adresse_serveur.sin_addr); // Adresse IP du serveur
     
        // Connexion au serveur
        if (connect(socket_client, (struct sockaddr*)&adresse_serveur, sizeof(adresse_serveur)) == -1) {
            perror("connect");
            exit(-1);
        }
     
        return socket_client;
    }
     
    // Fonction pour faire dormir le programme pendant un certain nombre de secondes
    // en affichant un compte à rebours à l'écran
    void sleep_with_countdown(int seconds) {
        // Afficher un message indiquant que le client est en attente pendant le nombre de secondes spécifié
        printf("En attente pendant %d secondes...\n", seconds);
     
        // Définir le temps de départ
        time_t start = time(NULL);
     
        // Répéter jusqu'à ce que le nombre de secondes spécifié soit écoulé
        while (time(NULL) - start < seconds) {
            // Calculer le temps restant en secondes
            int remaining = seconds - (time(NULL) - start);
     
            // Afficher le temps restant sur la ligne précédente
            printf("Temps restant : %d secondes", remaining);
     
            // Dormir pendant 1 seconde
            sleep(1);
        }
    }
     
    int main(int argc, char* argv[]) {
        int socket_client;   // Numéro de la socket client
        int port;            // Numéro de port du serveur
        char* adresse_ip;    // Adresse IP du serveur
        char buffer[32];     // Buffer pour stocker la réponse du serveur
     
        // Vérifier le nombre d'arguments
        if (argc != 3) {
            printf("Usage: %s adresse_ip port\n", argv[0]);
            exit(-1);
        }
     
        // Récupérer l'adresse IP et le numéro de port
        adresse_ip = argv[1];
        port = atoi(argv[2]);
     
        // Créer la socket client
        socket_client = creer_socket_client(adresse_ip, port);
     
        // Afficher un message indiquant que la connexion avec le serveur est établie
        printf("Connexion avec le serveur établie...\n");
     
        // BOUCLE PRINCIPALE
        while (1) {
            // Envoyer une demande d'état au serveur
            write(socket_client, "ETAT", strlen("ETAT") + 1);
     
            // Afficher un message indiquant que la demande d'état a été envoyée
            printf("Demande d'état envoyée...\n");
     
            // Afficher un message indiquant que le client est en train d'attendre la réponse du serveur
            printf("En attente de la réponse du serveur...\n");
     
            // Attendre la réponse du serveur
            if (read(socket_client, buffer, sizeof(buffer)) == -1) {
                perror("read");
                exit(-1);
            }
     
            // Afficher la réponse du serveur (son PID)
            printf("Réponse du serveur : %s\n", buffer);
     
            // Utiliser la fonction sleep_with_countdown() pour afficher un compte à rebours de 30 secondes
    		// avant de continuer avec la boucle
    		sleep_with_countdown(30);
        }
     
        // Fermer la socket client
        close(socket_client);
     
        return 0;
    }
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    #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 <arpa/inet.h>
     
    // Fonction pour la création d'un socket serveur
    int creer_socket_serveur(int port) {
        int socket_serveur;
        int optval = 1;
        struct sockaddr_in adresse_locale;
        char adresse_ip[INET_ADDRSTRLEN];
     
        // Création de la socket
        socket_serveur = socket(AF_INET, SOCK_STREAM, 0);
        if (socket_serveur == -1) {
            perror("socket serveur");
            exit(-1);
        }
     
        // On rend le port réutilisable rapidement après un arrêt brutal
        if (setsockopt(socket_serveur, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(int)) == -1) {
            perror("setsockopt");
            exit(-1);
        }
     
        // Configuration de la socket
        adresse_locale.sin_family = AF_INET;
        adresse_locale.sin_port = htons(port);
        adresse_locale.sin_addr.s_addr = INADDR_ANY;
        if (bind(socket_serveur, (struct sockaddr*)&adresse_locale, sizeof(adresse_locale)) == -1) {
            perror("bind serveur");
            exit(-1);
        }
     
        // Afficher l'adresse IP d'écoute choisie par défaut
    	if (inet_ntop(AF_INET, &adresse_locale.sin_addr, adresse_ip, INET_ADDRSTRLEN) == NULL) {
        	perror("inet_ntop");
        	exit(-1);
    	}
    	printf("Adresse IP d'écoute : %s\n", adresse_ip);
     
    	// Afficher le port d'écoute choisi par défaut
    	port = ntohs(adresse_locale.sin_port);
    	printf("Port d'écoute : %d\n", port);
     
    	// Démarrage du listage
    	if (listen(socket_serveur, 10) == -1) {
        	perror("listen serveur");
        	exit(-1);
    	}
     
    	return socket_serveur;
    }
     
     
     
    int main(int argc, char* argv[]) {
        int socket_serveur;
        int socket_client;
        int port;
        socklen_t longueur_adresse_courante;
        struct sockaddr_in adresse_client_courant;
        char buffer[32];
     
        // Vérifier le nombre d'arguments
        if (argc != 2) {
            printf("Usage: %s port\n", argv[0]);
            exit(-1);
        }
     
        // Récupérer le numéro de port
        port = atoi(argv[1]);
     
        // Créer la socket serveur
        socket_serveur = creer_socket_serveur(port);
     
        // Afficher un message indiquant que le serveur est en écoute
        printf("Le serveur est en écoute sur le port %d...\n", port);
     
        // BOUCLE PRINCIPALE
    	while (1) {
        // Attendre une connexion client
        longueur_adresse_courante = sizeof(adresse_client_courant);
        socket_client = accept(socket_serveur, (struct sockaddr*)&adresse_client_courant, &longueur_adresse_courante);
        if (socket_client == -1) {
            perror("accept");
            exit(-1);
        }
     
        // Afficher un message indiquant qu'un client s'est connecté
        printf("Un client s'est connecté...\n");
     
        // BOUCLE DE TRAITEMENT
        while (1) {
            // Lire la demande du client
            if (read(socket_client, buffer, sizeof(buffer)) == -1) {
                perror("read");
                exit(-1);
            }
     
            // Vérifier si c'est une demande d'état
    		if (strcmp(buffer, "ETAT") == 0) {
    		// Répondre en envoyant le PID du serveur
    		sprintf(buffer, "%d", getpid());
    		write(socket_client, buffer, strlen(buffer) + 1);
    		} else {
    		// Sinon, ignorer la demande
    		}	
     
    		// Fermer la socket client
    		close(socket_client);
     
    		// Afficher un message indiquant que la connexion avec le client est fermée
    		printf("Connexion avec le client fermée.\n");
    		}
    	}
     
    	// Fermer la socket serveur
    	close(socket_serveur);
     
    	return 0;
    }
    Fichiers attachés Fichiers attachés

  2. #2
    Membre chevronné
    Profil pro
    Inscrit en
    Mai 2006
    Messages
    721
    Détails du profil
    Informations personnelles :
    Localisation : Belgique

    Informations forums :
    Inscription : Mai 2006
    Messages : 721
    Points : 1 876
    Points
    1 876
    Par défaut
    Bonjour,

    Il faudrait ajouter un moyen de débugger votre programme, par exemple obtenir un stacktrace pour voir la ligne exacte où ça échoue, et le détail de l'erreur. Ça faciliterait les choses et ça vous éviterait de deviner l'origine du problème.

    Je n'ai pas testé le code et c'est très spéculatif de ma part, mais vu que le message "Connexion avec le client fermée" apparaît je suis tentée de dire que le problème vient de l'instruction suivante:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    	// Fermer la socket serveur
    	close(socket_serveur);
    Toutefois, je pense qu'il y a peut-être un petit problème de logique. Dans votre boucle, vous faites:
    Mais vous le faites de manière indiscriminée. Car si la boucle continue et reprend depuis le début, ce socket socket_client peut être détruit. Et alors le read échoue logiquement.
    Autrement dit, la première passe de la boucle se passe normalement, mais peut-être que la suivante échoue. C'est facile à vérifier, rajouter quelques printf dans votre boucle avec un compteur d'itération. Et vérifier l'état du socket, l'objet est-il toujours instancié ou détruit ?

    Extrait du code incriminé:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
        // BOUCLE DE TRAITEMENT
        while (1) {
            // Lire la demande du client
            if (read(socket_client, buffer, sizeof(buffer)) == -1) {
                perror("read");
                exit(-1);
            }
     
            // Vérifier si c'est une demande d'état
    		if (strcmp(buffer, "ETAT") == 0) {
    		// Répondre en envoyant le PID du serveur
    		sprintf(buffer, "%d", getpid());
    		write(socket_client, buffer, strlen(buffer) + 1);
    		} else {
    		// Sinon, ignorer la demande
    		}	
     
    		// Fermer la socket client
    		close(socket_client);
     
    		// Afficher un message indiquant que la connexion avec le client est fermée
    		printf("Connexion avec le client fermée.\n");
    		}
    	}
     
    	// Fermer la socket serveur
    	close(socket_serveur);
     
    	return 0;
    }

  3. #3
    Rédacteur/Modérateur


    Homme Profil pro
    Network game programmer
    Inscrit en
    Juin 2010
    Messages
    7 115
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 36
    Localisation : Canada

    Informations professionnelles :
    Activité : Network game programmer

    Informations forums :
    Inscription : Juin 2010
    Messages : 7 115
    Points : 32 965
    Points
    32 965
    Billets dans le blog
    4
    Par défaut
    Ton code fait litérallement accept > read > close > read. Donc forcément le deuxième read échoue et vu que ta gestion d'erreur c'est un texte vide de sens et un exit violent, ton programme fait exactement ce que tu lui as indiqué de faire.
    Heureusement on a inventé les debuggers, et sur Linux on a surtout errno qu'il convient d'afficher pour savoir ce qu'il s'est passé.
    Pensez à consulter la FAQ ou les cours et tutoriels de la section C++.
    Un peu de programmation réseau ?
    Aucune aide via MP ne sera dispensée. Merci d'utiliser les forums prévus à cet effet.

  4. #4
    Nouveau Candidat au Club
    Homme Profil pro
    En reconversion
    Inscrit en
    Septembre 2022
    Messages
    2
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Pyrénées Orientales (Languedoc Roussillon)

    Informations professionnelles :
    Activité : En reconversion

    Informations forums :
    Inscription : Septembre 2022
    Messages : 2
    Points : 1
    Points
    1
    Par défaut
    Citation Envoyé par Bousk Voir le message
    Ton code fait litérallement accept > read > close > read. Donc forcément le deuxième read échoue
    Bonjour Bousk,

    J'imagine que tu parle du code côté serveur ?

  5. #5
    Expert éminent sénior
    Avatar de Sve@r
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Février 2006
    Messages
    12 685
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Oise (Picardie)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : Aéronautique - Marine - Espace - Armement

    Informations forums :
    Inscription : Février 2006
    Messages : 12 685
    Points : 30 974
    Points
    30 974
    Billets dans le blog
    1
    Par défaut
    Bonjour
    Citation Envoyé par Axylum67 Voir le message
    Actuellement en train d'essayer de comprendre les sockets via le langage C sous Linux ( échange des données à travers une connexion socket), j'ai d'abord créé un fichier serveur.c qui prend en argument le numéro de port d’écoute et, en option, l’adresse IPv4 d’écoute et un fichier client.c qui prend en argument le numéro de port et l’adresse IPv4 du serveur.
    Il y a un point essentiel à comprendre pour le serveur: soit il écoute le réseau, soit il dialogue avec le client. Mais il ne peut pas faire les deux à la fois.
    C'est pour ça que quand on crée un serveur, dès qu'un client arrive, on lui fait générér un fils via fork() et c'est le fils qui est en charge du dialogue tandis que le père retourne écouter. Mais faut bien gérer les canaux à fermer (car un seveur en a deux: le canal qui sert à la connexion du client et celui qui sert au dialogue).

    Voici un exemple full fonctionnel d'un tel client/serveur
    serveur.c
    Code c : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
    179
    180
    181
    182
    183
    184
    185
    186
    187
    188
    189
    190
    191
    192
    193
    194
    195
    196
    197
    198
    199
    200
    201
    202
    203
    204
    205
    206
    207
    208
    209
    210
    211
    212
    213
    214
    215
    216
    217
    218
    219
    220
    221
    222
    223
    224
    225
    226
    227
    228
    229
    230
    231
    232
    233
    234
    235
    //
    // Serveur réseau mode tcp
    // Programme destiné à écouter un client via une connexion mode tcp
    // Usage: prog [port]
    //
     
    #include <sys/types.h>				// Types prédéfinis "c"
    #include <sys/socket.h>				// Généralités sockets
    #include <sys/param.h>				// Paramètres et limites système
    #include <netinet/in.h>				// Spécifications socket internet
    #include <arpa/inet.h>				// Adresses format "arpanet"
    #include <signal.h>				// Signaux de communication
    #include <netdb.h>				// Gestion network database
    #include <stdio.h>				// I/O fichiers classiques
    #include <string.h>				// Gestion chaines de caractères
    #include <stdlib.h>				// Librairie standard Unix
    #include <unistd.h>				// Standards Unix
    #include <errno.h>				// Erreurs système
    #include "socket_tcp.h"				// Outils communs client et serveur
     
    // Structure de travail socket
    typedef struct {
    	int sk_connect;				// Socket de connexion
    	int sk_dialog;				// Socket de dialogue
    } t_socket;					// Type créé
     
    // Fonctions diverses
    int init_socket(unsigned short);		// Initialisation socket
    int client(t_socket*);				// Gestion client
    int dialogue(int);				// Dialogue avec le client
    int nslookup(struct sockaddr_in*, char*);	// Conversion adresse en nom
     
    // Programme principal
    int main(int argc, char* argv[]) {
    	// Déclaration des variables
    	char hostname[MAXHOSTNAMELEN + 1];	// Nom machine locale
    	t_socket sock;				// Socket
     
    	// Récuperation nom machine locale (juste pour l'exemple)
    	if (gethostname(hostname, MAXHOSTNAMELEN) != 0)
    		fprintf(stderr, "ligne %u - gethostname(%s) - %s\n", __LINE__, hostname, strerror(errno));
    	printf("gethostname='%s'\n", hostname);
     
    	// Détournement du signal émis à la mort du fils (il ne reste pas zombie)
    	signal(SIGCHLD, SIG_IGN);
     
    	// Initialisation socket de connexion
    	if ((sock.sk_connect=init_socket(argc > 1 ?atoi(argv[1]) :0)) < 0) {
    		fprintf(stderr, "ligne %u - init_socket() - %s\n", __LINE__, strerror(errno));
    		return errno;
    	}
    	printf("Socket de connexion (%d) initialisée\n", sock.sk_connect);
     
    	// Ecoute de la ligne
    	if (listen(sock.sk_connect, 1) < 0) {
    		fprintf(stderr, "ligne %u - listen() - %s\n", __LINE__, strerror(errno));
    		return errno;
    	}
    	printf("Socket de connexion (%d) en écoute\n", sock.sk_connect);
     
    	// Attente permanente
    	fputc('\n', stdout);
    	while (1) {
    		printf("ppid=%u, pid=%u, socket=%d, attente entrée...\n", getppid(), getpid(), sock.sk_connect);
    		// Attente connexion client
    		if (client(&sock) < 0) {
    			fprintf(stderr, "ligne %u - client() - %s\n", __LINE__, strerror(errno));
    			continue;
    		}
    	}
    	// Pas de sortie de programme - Boucle infinie
     
    	// Fermeture socket et fin théorique du programme (pour être propre)
    	close(sock.sk_connect);
    	return 0;
    }
     
    // Initialisation socket
    int init_socket(unsigned short port) {
    	// Déclaration des variables
    	ushort i;				// Indice de boucle
    	struct servent* service_info;		// Info. service demandé
    	struct sockaddr_in adr_serveur;		// Adresse socket serveur
    	int sock;				// Socket de connexion
     
    	// Si le port n'est pas fourni
    	if (port == 0) {
    		// Récuperation port dans "/etc/services"
    		if ((service_info=getservbyname(SERVICE_LABEL, SERVICE_PROTOCOL)) == NULL) {
    			fprintf(stderr, "ligne %u - getservbyname(%s, %s) - %s\n", __LINE__, SERVICE_LABEL, SERVICE_PROTOCOL, strerror(errno));
    			return -1;
    		}
    		port=ntohs(service_info->s_port);
     
    		fputc('\n', stdout);
    		printf("service_name='%s'\n", service_info->s_name);
    		for (i=0; service_info->s_aliases[i] != NULL; i++)
    			printf("service_s_aliase[%hu]='%s'\n", i, service_info->s_aliases[i]);
    		printf("service_port=%hu\n", ntohs(service_info->s_port));
    		printf("service_protocole='%s'\n", service_info->s_proto);
    	} else
    		printf("port demandé=%hu\n", port);
     
    	// Création socket
    	if ((sock=socket(AF_INET, SOCK_STREAM, 0)) < 0) {
    		fprintf(stderr, "ligne %u - socket() - %s\n", __LINE__, strerror(errno));
    		return -2;
    	}
    	printf("Socket de connexion (%d) créée\n", sock);
     
    	// Remplissage adresse socket
    	memset(&adr_serveur, 0, sizeof(struct sockaddr_in));
    	adr_serveur.sin_family=AF_INET;
    	adr_serveur.sin_port=htons(port);
    	adr_serveur.sin_addr.s_addr=INADDR_ANY;
     
    	// Identification socket/réseau
    	if (bind(sock, (struct sockaddr*)&adr_serveur, sizeof(struct sockaddr_in)) < 0) {
    		fprintf(stderr, "ligne %u - bind() - %s\n", __LINE__, strerror(errno));
    		return -3;
    	}
    	printf("Socket de connexion (%d) identifiée sur le réseau\n", sock);
     
    	// Renvoi socket créée
    	return sock;
    }
     
    // Gestion du client
    int client(t_socket* sock) {
    	// Déclaration des variables
    	int pid;				// Process créé
    	struct sockaddr_in adr_client;		// Adresse socket client
    	socklen_t len_adr;			// Taille adresse
    	char lookup[MAXHOSTNAMELEN + 1024 + 1]; // Correspondance adresse/nom
     
    	// Attente connexion client
    	len_adr=sizeof(struct sockaddr_in);
    	if ((sock->sk_dialog=accept(sock->sk_connect, (struct sockaddr*)&adr_client, &len_adr)) < 0) {
    		fprintf(stderr, "ligne %u - accept() - %s\n", __LINE__, strerror(errno));
    		return -1;
    	}
     
    	// Client connecté
    	if (nslookup(&adr_client, lookup) < 0)
    		fprintf(stderr, "ligne %u - nslookup() - %s\n", __LINE__, strerror(errno));
    	printf("ppid=%d, pid=%d, socket=%d - Entrée émise (dialogue=%d, adr=%s)\n", getppid(), getpid(), sock->sk_connect, sock->sk_dialog, lookup);
     
    	// Duplication du process (ne pas oublier de fflusher les flux standard)
    	fflush(stdout);
    	fflush(stderr);
    	switch (pid=fork()) {
    		case -1: // Erreur de fork
    			close(sock->sk_connect);
    			close(sock->sk_dialog);
    			fprintf(stderr, "ligne %u - fork() - %s\n", __LINE__, strerror(errno));
    			return -2;
    		case 0: // Fils
    			// Fermeture socket de connexion (inutilisée)
    			close(sock->sk_connect);
     
    			// Dialogue avec le client
    			if (dialogue(sock->sk_dialog) < 0) {
    				fprintf(stderr, "ligne %u - dialogue() - %s\n", __LINE__, strerror(errno));
    				exit(errno);
    			}
    			// Fin du fils
    			close(sock->sk_dialog);
    			printf("\tppid=%d, pid=%d, socket=%d - Entrée raccrochée\n", getppid(), getpid(), sock->sk_dialog);
    			exit(0);
    		default: // Père
    			// Fermeture socket de dialogue (inutilisée)
    			close(sock->sk_dialog);
    	}
     
    	// Fin fonction
    	return 0;
    }
     
    // Dialogue avec le client
    int dialogue(int sock) {
    	// Déclaration des variables
    	int sz_read;				// Nbre octets lus
    	char buf[SZ_BUF+1];			// Buffer texte
     
    	// Lecture en boucle sur la socket
    	while ((sz_read=read(sock, buf, SZ_BUF+1)) > 0) {
    		// Par précaution, le buffer reçu est transformé en chaine
    		buf[sz_read]='\0';
     
    		// Suppression du caractère '\n' éventuel
    		buf[strcspn(buf, "\n")]='\0';
     
    		// Mémorisation chaine contient "EOT" (optimisation)
    		char* pt=strcmp(buf, "EOT") != 0 ?buf :NULL;
     
    		// Affichage chaine reçue
    		printf("\tppid=%u, pid=%u, socket=%d - Le serveur a lu %d [%s]%s\n", getppid(), getpid(), sock, sz_read, buf, pt != NULL ?"" :" => Fin de communication");
     
    		// Si la chaine contient "EOT"
    		if (pt == NULL) break;
    	}
     
    	// Si l'arrêt de la lecture est dû à une erreur
    	if (sz_read < 0) {
    		fprintf(stderr, "ligne %u - read() - %s\n", __LINE__, strerror(errno));
    		return -1;
    	}
     
    	// Fin fonction
    	printf("\tppid=%u, pid=%u, socket=%d - Client terminé\n", getppid(), getpid(), sock);
    	return 0;
    }
     
    // Conversion adresse en nom
    int nslookup(struct sockaddr_in* adr, char* lookup) {
    	// Déclaration des variables
    	char* adr_ascii;			// Adresse client mode ascii
    	struct hostent* host_info;		// Informations host
     
    	// Transformation adresse net en ascii
    	if ((adr_ascii=inet_ntoa(adr->sin_addr)) == NULL) {
    		fprintf(stderr, "ligne %u - inet_ntoa() - %s\n", __LINE__, strerror(errno));
    		strcpy(lookup, "?");
    		return -1;
    	}
     
    	// Récupération informations sur host par son adresse
    	if ((host_info=gethostbyaddr(&adr->sin_addr.s_addr, sizeof(struct in_addr), AF_INET)) == NULL) {
    		fprintf(stderr, "ligne %u - gethostbyaddr() - %s\n", __LINE__, strerror(errno));
    		sprintf(lookup, "%s (?)", adr_ascii);
    		return -2;
    	}
    	sprintf(lookup, "%s (%s)", adr_ascii, host_info->h_name);
    	return 0;
    }

    client.c
    Code c : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    // Client réseau mode tcp
    // Programme destiné à écrire des chaines dans une connexion mode tcp
    // Usage: prog [nom serveur | adresse serveur [port]]
    //
    #include <sys/types.h>				// Types prédéfinis "c"
    #include <sys/socket.h>				// Généralités sockets
    #include <sys/param.h>				// Paramètres et limites système
    #include <netinet/in.h>				// Spécifications socket internet
    #include <stdio.h>				// I/O fichiers classiques
    #include <string.h>				// Gestion chaines de caractères
    #include <netdb.h>				// Gestion network database
    #include <stdlib.h>				// Librairie standard Unix
    #include <unistd.h>				// Standards Unix
    #include <errno.h>				// Erreurs système
    #include "socket_tcp.h"				// Outils communs client et serveur
    #define SERVEUR_DEFAULT ("localhost")		// Nom serveur utilisé par défaut
     
    typedef struct {
    	char* host;				// Adresse serveur
    	int port;				// Port serveur
    	struct sockaddr_in adr_serveur;		// Adresse socket serveur
    } t_param;
     
    // Fonctions diverses
    int init_socket(t_param*);			// Initialisation socket
     
    // Programme principal
    int main(int argc, char* argv[]) {
    	// Déclaration des variables
    	int sock;				// Socket
    	t_param param;				// Paramètres de connexion
    	char hostname[MAXHOSTNAMELEN + 1];	// Nom machine locale
    	char buf[SZ_BUF+1];			// Buffer texte
     
    	// Récuperation nom machine locale (juste pour l'exemple)
    	if (gethostname(hostname, MAXHOSTNAMELEN) != 0)
    		fprintf(stderr, "ligne %u - gethostname(%s) - %s\n", __LINE__, hostname, strerror(errno));
    	printf("gethostname='%s'\n", hostname);
     
    	// Initialisation socket selon les paramètres entrés
    	switch (argc) {
    		case 1: // Pas de paramètre
    			param.host=SERVEUR_DEFAULT;
    			param.port=0;
    			break;
    		case 2: // Un paramètre: le serveur
    			param.host=argv[1];
    			param.port=0;
    			break;
    		default: // Au-moins 2 paramètres: le serveur et le port
    			param.host=argv[1];
    			param.port=atoi(argv[2]);
    	}
    	if ((sock=init_socket(&param)) < 0) {
    		fprintf(stderr, "ligne %u - init_socket(%s) - %s\n", __LINE__, param.host,
    		strerror(errno));
    		return errno;
    	}
    	printf("Initialisation socket (serveur=%s, port=%hu, socket=%d)\n", param.host, param.port, sock);
     
    	// Saisie et envoi de la chaîne en boucle
    	do {
    		// Saisie de la chaîne
    		fputs("Entrer chaine (EOT pour finir) :", stdout); fflush(stdout);
    		fgets(buf, SZ_BUF+1, stdin);
     
    		// Suppression du caractère '\n' éventuel
    		buf[strcspn(buf, "\n")]='\0';
     
    		// Envoi de la chaîne sur la socket (ne pas oublier le '\0')
    		if (write(sock, buf, strlen(buf) + 1) < 0)
    			fprintf(stderr, "ligne %u - write(%s) - %s\n", __LINE__, buf, strerror(errno));
    	} while (strcmp(buf, "EOT") != 0);
     
    	// Fermeture socket et fin du programme
    	close(sock);
    	return 0;
    }
     
    // Initialisation socket
    int init_socket(t_param* param) {
    	// Déclaration des variables
    	ushort i;				// Indice de boucle
    	ushort j;				// Indice de boucle
    	struct sockaddr_in adr_serveur;		// Adresse socket serveur
    	struct hostent* host_info;		// Info. host
    	struct servent* service_info;		// Info. service demandé
    	int sock;				// Socket
     
    	// Récuperation informations serveur
    	if ((host_info=gethostbyname(param->host)) == NULL) {
    		fprintf(stderr, "ligne %u - gethostbyname(%s) - %s\n", __LINE__, param->host, strerror(errno));
    		return -1;
    	}
    	fputc('\n', stdout);
    	printf("host_info.h_name='%s'\n", host_info->h_name);
    	for (i=0; host_info->h_aliases[i] != NULL; i++)
    		printf("host_info.h_aliase[%hu]='%s'\n", i, host_info->h_aliases[i]);
    	printf("host_info.h_addrtype=%u\n", host_info->h_addrtype);
    	printf("host_info.h_length=%u\n", host_info->h_length);
    	for (i=0; host_info->h_addr_list[i] != NULL; i++) {
    		printf("host_info.h_addr_list[%hu]=", i);
    		for (j=0; j < host_info->h_length; j++)
    			printf("%hu ", (unsigned char)host_info->h_addr_list[i][j]);
    		fputc('\n', stdout);
    	}
     
    	// Si le port n'est pas fourni
    	if (param->port == 0) {
    		// Récuperation port dans "/etc/services"
    		if ((service_info=getservbyname(SERVICE_LABEL, SERVICE_PROTOCOL)) ==NULL) {
    			fprintf(stderr, "ligne %u - getservbyname(%s, %s) - %s\n", __LINE__, SERVICE_LABEL, SERVICE_PROTOCOL, strerror(errno));
    			return -2;
    		}
    		param->port=ntohs(service_info->s_port);
    		fputc('\n', stdout);
    		printf("service_name='%s'\n", service_info->s_name);
    		for (i=0; service_info->s_aliases[i] != NULL; i++)
    			printf("service_s_aliase[%hu]='%s'\n", i, service_info->s_aliases[i]);
    		printf("service_port=%hu\n", ntohs(service_info->s_port));
    		printf("service_protocole='%s'\n", service_info->s_proto);
    	} else
    		printf("port demandé=%hu\n", param->port);
     
    	// Remplissage adresse socket
    	memset(&adr_serveur, 0, sizeof(struct sockaddr_in));
    	adr_serveur.sin_family=AF_INET;
    	adr_serveur.sin_port=htons(param->port);
    	memcpy(&adr_serveur.sin_addr.s_addr, host_info->h_addr, host_info->h_length);
     
    	// Création socket
    	if ((sock=socket(AF_INET, SOCK_STREAM, 0)) < 0) {
    		fprintf(stderr, "ligne %u - socket() - %s\n", __LINE__, strerror(errno));
    		return -3;
    	}
    	printf("Socket (%d) créée\n", sock);
     
    	// Tentative de connexion en boucle permanente
    	fputc('\n', stdout);
    	while (1) {
    		// Connexion au serveur
    		if (connect(sock, (struct sockaddr*)&adr_serveur, sizeof(struct sockaddr_in)) == 0)
    			break;
    		fprintf(stderr, "ligne %u - connect() - %s\n", __LINE__, strerror(errno));
    		sleep(5);
    	}
    	printf("Connexion réussie (socket=%d)\n", sock);
     
    	// Fin fonction
    	return sock;
    }

    header "socket_tcp.h"
    Code c : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    //
    // Header commun aux programmes de communication via tcp
    //
    #ifndef _SOCKET_TCP_H_
    #define _SOCKET_TCP_H_
    #define SERVICE_LABEL ("essai")			// Nom service requis
    #define SERVICE_PROTOCOL ("tcp")		// Protocole service requis
    #define SZ_BUF (256)				// Taille buffer
    #endif //_SOCKET_TCP_H

    Mise en oeuvre: après avoir compilé les deux sources, tu lances le serveur en lui donnant le port à écouter (ex ./serveur 12345). Puis sur un autre terminal tu lances le client en lui donnant l'adresse serveur (ou son nom) et le numéro de port (ex ./client localhost 12345).
    Si ça marche alors le serveur t'informe de l'arrivée du client et se remet en écoute. Et du côté client, dès qu'il est connecté il te propose une saisie dans laquelle tu tapes ce que tu veux, tout ce que tu tapes est affiché sur le serveur. Et quand tu en as marre, tu tapes "EOT".
    Tu peux lancer autant de clients que tu veux depuis différents terminaux (voire même depuis une autre machine si tu as un réseau interne), chaque client sera identifié sur le serveur.
    Et pour arrêter le serveur, un "ctrl-c".

    Tu peux aussi rajouter la ligne essai 5000/tcp dans le fichier "/etc/services" et dans ce cas tu n'es plus obligé de donner le numéro de port, le programme le récupère automatiquement via la string "essai" définie dans le ".h" (ici 5000 mais tu mets le numéro que tu veux).
    Mon Tutoriel sur la programmation «Python»
    Mon Tutoriel sur la programmation «Shell»
    Sinon il y en a pleins d'autres. N'oubliez pas non plus les différentes faq disponibles sur ce site
    Et on poste ses codes entre balises [code] et [/code]

Discussions similaires

  1. Serveur SVN sur windows et Client SVN sur Linux
    Par nicolas.c...o dans le forum Subversion
    Réponses: 0
    Dernier message: 12/02/2010, 18h03
  2. creer un daemon pour serveur sur linux
    Par timtima dans le forum POSIX
    Réponses: 8
    Dernier message: 19/12/2007, 02h31
  3. Réponses: 2
    Dernier message: 14/05/2007, 22h35
  4. [Reseau] probleme client/serveur TCP LInux
    Par jmjmjm dans le forum Réseau
    Réponses: 20
    Dernier message: 03/12/2006, 19h32
  5. Crére un client/serveur sous linux en TCP
    Par Darknicosh dans le forum Développement
    Réponses: 11
    Dernier message: 20/06/2005, 10h19

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo