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 :

server TCP, thread et recv


Sujet :

Réseau C

  1. #1
    Nouveau Candidat au Club
    Homme Profil pro
    Infographiste 3D et dév
    Inscrit en
    Avril 2016
    Messages
    6
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 41
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Infographiste 3D et dév

    Informations forums :
    Inscription : Avril 2016
    Messages : 6
    Points : 1
    Points
    1
    Par défaut server TCP, thread et recv
    Bonjour à tous,

    Je viens vers vous, après moultes recherches, car je ne trouve pas la solution.
    Je suis débutant en C et pour un projet de formation je dois faire un serveur TCP avec des threads, mais mon soucis c'est que je n'arrive pas à m'en sortir avec les fonctions recv() et send() qui me retourne sans cesse un bad file descriptor.
    Je vous met le code serveur ( soyez indulgent )

    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
    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
    // Nom: Serveur TCP/IP
     
    #include <stdio.h>			// perror()
    #include <netinet/in.h>		// sockaddr_in et INADDR_ANY
    #include <sys/socket.h>		// socket(), bind(), recv(), send()
    #include <sys/types.h>		// socket(), bind(), recv(), send(), wait()
    #include <string.h>			// memset()
    #include <signal.h>			// signal()
    #include <wait.h>			// wait()
    #include <pthread.h> 		// pour les thread
    #include <stdlib.h>			// pour malloc()
     
    // Pour open()
    #include <sys/types.h>
    #include <sys/stat.h>
    #include <fcntl.h>
     
    // Definition des constantes symboliques
    #define TAILLE_FILE_DATTENTE_CONNEXION 5 // Taille de la file d'attente de demandes de connexion
    #define NUMERO_PORT_SERVEUR 30000
     
    pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;		// Création du mutex
    pthread_cond_t condition = PTHREAD_COND_INITIALIZER; 	// Création de la condition
     
    // routine d'interception du signal SIGCHLD emis par les fils
    void intercepterSignalSIGCHLD(int numeroSignalRecu);
     
    // prototypes des fonctions
    //int BougerRobot(int *socketConnectee);
    static void *BougerRobot(void *socket);
    static void *visualiser(void * argument);
     
    int main()
    {
    	// couple adresse IP, numero de port serveur
    	struct sockaddr_in coupleAdresseIPNumeroPortServeur;
     
    	// couple adresse IP, numero de port client
    	struct sockaddr_in coupleAdresseIPNumeroPortClient;
    	int socketDemandeDeConnexion; // socket permettant d'effectuer une demande de connexion
    	int socketConnectee; // socket dediee a une connexion etablie
    	socklen_t longueurClient; // longueur du couple adresse IP, numero de port du client
    	int optval;
     
    	// Mise en place de l'interception de SIGCHLD emis par les fils
    	signal(SIGCHLD,intercepterSignalSIGCHLD);
     
    	// Initialiser les structures a des octets de valeurs
    	memset(&coupleAdresseIPNumeroPortServeur,0,sizeof(struct sockaddr_in));
    	memset(&coupleAdresseIPNumeroPortClient,0,sizeof(struct sockaddr_in));
     
    	// Creer la socket serveur en mode TCP
    	if ((socketDemandeDeConnexion = socket(PF_INET,SOCK_STREAM,0)) < 0)
    	{
    		perror("socket");
    		return -1;
    	}
     
    	// Reutiliser le meme port en cas d'interruption brutal du serveur
    	optval = 1;
    	setsockopt(socketDemandeDeConnexion, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof optval);
     
    	// Associer la socket serveur a un couple adresse IP, numero de port socket du domaine reseau
    	coupleAdresseIPNumeroPortServeur.sin_family = AF_INET;
     
    	// INADDR_ANY pour accepter les requetes de toutes les interfaces reseau du serveur
    	coupleAdresseIPNumeroPortServeur.sin_addr.s_addr = htonl(INADDR_ANY);
     
    	// affecter le numero de pott de l'application
    	coupleAdresseIPNumeroPortServeur.sin_port = htons(NUMERO_PORT_SERVEUR);
    	if (bind(socketDemandeDeConnexion,(struct sockaddr *)(&coupleAdresseIPNumeroPortServeur),sizeof(struct sockaddr_in)) < 0)
    	{
    		perror("bind");
    		return -1;
    	}
     
    	// Creer une file d'attente de demandes de connexion
    	if (listen(socketDemandeDeConnexion,TAILLE_FILE_DATTENTE_CONNEXION) <0)
    	{
    		perror("listen");
    		return -1;
    	}
    	longueurClient = sizeof(struct sockaddr_in);
     
    	// Création des variables de thread
    	pthread_t idThreadBougerRobot; // Identifiant du thread bouger
    	pthread_t idThreadVisualisation; // Identifiant du thread visualiser
     
    	// Variables identification du client
    	char idThread[2];
    	int idT;
     
    	while(1)
    	{
    		// Attendre une demande de connexion du client
    		socketConnectee = accept(socketDemandeDeConnexion,(struct sockaddr *)(&coupleAdresseIPNumeroPortClient),&longueurClient);
    		if (socketConnectee < 0)
    		{
    			perror ("accept");
    		}
    		else
    		{
    			idT = recv(socketConnectee, idThread, sizeof(idThread), 0);
    			printf("Client: %d\n", idT);
    			if(idT == 1)
    			{
    				// Créer le premier thread
    				if (pthread_create (&idThreadBougerRobot, NULL, BougerRobot, (void *) &socketConnectee) != 0)
    				{
    					perror("pthread_create() bouger:");
    					return -1;
    				}
    				// pthread_join(idThreadBougerRobot, NULL);
    				printf("Thread bouger robot, créé.\n");
    			}
    			if (idT == 2)
    			{
    				// Créer le second thread
    				if (pthread_create (&idThreadVisualisation, NULL, visualiser, NULL) != 0)
    				{
    					perror("pthread_create() afficher:");
    					return -1;
    				}
    				printf("Thread visualisation unity, créé.\n");
    			}
     
    			// dans le pere fermer dans le pere la socket connectee
    			if(close(socketConnectee) < 0)
    			{
    				perror("close");
    				return -1;
    			}
    		}
    	}
    	return 0;
    }
     
    void intercepterSignalSIGCHLD(int numeroSignalRecu)
    {
    	wait(NULL);
    }
     
    static void *BougerRobot(void *socket)
    {
    	char lettre;
    	char *lettreReponse;
    	int pvsocket = *(int *) socket;
    	printf("%d\n", pvsocket);
     
    	// Recevoir la requête
    	if (recv(pvsocket, &lettre, sizeof(lettre), 0) == -1)
    	{
    		perror("recvfrom");
    	}
     
    	// Traiter la requete
    	*lettreReponse = lettre + 1;
    	printf("%c\n", *lettreReponse);
     
    	// Envoyer la réponse
    	if (send(pvsocket, &lettreReponse, sizeof(lettreReponse), 0) == -1)
    	{
    		perror("sendto");
    	}
     
    }
     
    static void *visualiser(void * argument)
    {
    	int compteur = 0, nombre = 0;
        srand(time(NULL));
     
        while(1) /* Boucle infinie */
        {
            nombre = rand()%10; /* On tire un nombre entre 0 et 10 */
            compteur += nombre; /* On ajoute ce nombre à la variable compteur */
            printf("\n%d", compteur);
            if(compteur >= 20) /* Si compteur est plus grand ou égal à 20 */
            {
                pthread_mutex_lock (&mutex); /* On verrouille le mutex */
                pthread_cond_signal (&condition); /* On délivre le signal : condition remplie */
                pthread_mutex_unlock (&mutex); /* On déverrouille le mutex */
                compteur = 0; /* On remet la variable compteur à 0 */
            }
            sleep (1); /* On laisse 1 seconde de repos */
        }
        pthread_exit(NULL); /* Fin du thread */
    }

    Voici le code client qui utilise le thread pour le changement de lettre ( c'est un exercice, ça n'a pas grand intérêt ) :
    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
    // clientMinimumTcp.c
    // envoie une lettre au serveur et reçoit la lettre suivante dans l'alphabet
    // clientMinimumTcp <adresse_IP_serveur>
    // clientMinimumTcp 192.168.10.1
     
    #include <stdio.h>			// perror(), printf(), fprintf(), scanf() 
    #include <arpa/inet.h>		// inet_pton(), htons()
    #include <sys/socket.h>		// socket(), recv(), send(), connect()
    #include <sys/types.h>		// socket(), recv(), send(), connect()
    #include <string.h>			// memset() 
     
    #define NUMERO_PORT_SERVEUR 30000
     
    int main(int argc, char * argv[])
    {
    	int i = 1;
     
    	/* couple adresse IP, numero de port serveur */
    	struct sockaddr_in coupleAdresseIPNumeroPortServeur;
    	int socketClient; /* socket client */
    	int longueurReponse; /* longueur de la reponse recue */
    	socklen_t longueurServeur; /* longueur du couple adresse IP, numero de port	du serveur */
    	long int adresseIPServeur; /* adresse IP du Serveur */
    	char lettre, lettreReponse;
     
    	/* Tester le nombre d'arguments */
    	if (argc != 2)
    	{
    		fprintf(stderr,"usage : clientMinimumTcp <adresse_IP_serveur>\nexemple : clientMinimumTcp 192.168.10.1\n");
    		return -1;
    	}
     
    	/* Initialiser les structures a des octets de valeurs */
    	memset(&coupleAdresseIPNumeroPortServeur,0,sizeof(struct sockaddr_in));
     
    	/* Initialiser l'adresse IP et le numero de port du serveur */
    	/* adresse IP au format reseau */
    	if (inet_pton(AF_INET, argv[1], (void *)&adresseIPServeur) < 0)
    	{
    		perror("inet_pton");
    		return -1;
    	}
     
    	/* socket du domaine reseau */
    	coupleAdresseIPNumeroPortServeur.sin_family = AF_INET;
     
    	/* adresse IP */
    	coupleAdresseIPNumeroPortServeur.sin_addr.s_addr = adresseIPServeur;
     
    	/* affecter le numero de port de l'application */
    	coupleAdresseIPNumeroPortServeur.sin_port = htons(NUMERO_PORT_SERVEUR);
     
    	/* Creer la socket client en mode TCP */
    	if ((socketClient = socket(PF_INET,SOCK_STREAM,0)) < 0)
    	{
    		perror("socket");
    		return -1;
    	}
     
    	/* Effectuer une demande de connexion */
    	longueurServeur = sizeof(struct sockaddr_in);
    	if (connect(socketClient, (struct sockaddr *)&coupleAdresseIPNumeroPortServeur,	longueurServeur) < 0)
    	{
    		perror("connect");
    		return -1;
    	}
     
    	// Envoyer la requete identification client
    	char connectionClient[1] = "c";
    	if (send(socketClient, &connectionClient, sizeof(connectionClient), 0) == -1)
    	{
    		perror("sendto");
    		return -1;
    	}
     
    	// Boucle de requêtes
    	for ( i ; i <= 10 ; i++)
    	{
    		/* demander à l'utilisateur de formuler une requete */
    		printf("Entrez une lettre :");
    		scanf("%c",&lettre);
     
    		/* Envoyer la requete */
    		if (send(socketClient, &lettre, sizeof(lettre), 0) == -1)
    		{
    			perror("sendto");
    			return -1;
    		}
     
    		/* Recevoir la reponse */
    		if (recv(socketClient,&lettreReponse, sizeof(unsigned char),0) == -1)
    		{
    			perror("recvfrom");
    			return -1;
    		}
     
    		/* Afficher la reponse */
    		printf("J'ai émis : %c, j'ai reçu : %c\n",lettre, lettreReponse);
    		fgetc(stdin); // On vide le cache mémoire
    	}
     
    	/* Fermer la connexion */
    	if (close(socketClient) < 0)
    	{
    		perror("close");
    		return -1;
    	}
    	return 0;
    }

    Merci pour votre aide car je deviens

  2. #2
    Expert confirmé
    Inscrit en
    Mars 2005
    Messages
    1 431
    Détails du profil
    Informations forums :
    Inscription : Mars 2005
    Messages : 1 431
    Points : 4 182
    Points
    4 182
    Par défaut
    Je lis d'une part « réseau et threads » et de l'autre « débutant en C », j'ai déjà le sourcil qui se lève. Le serveur ferme la connexion dès après le premier recv, il n'y a aucun mécanisme de synchronisation des ressources critiques (sockets), et ce n'est que le bout de l'iceberg...

    Tu es parti d'un code single thread qui fonctionne pour arriver à cela ? Parce qu'en l'état on ne devine pas ce que tu veux faire, c'est très confus et il y a beaucoup de code parasite. Oublie le threading, vas-y vraiment par étapes. Envoie un message au serveur qui l'affiche (communication unidirectionnelle), puis ajoute une réponse (communication bidirectionnelle), puis un handshake et un protocole d'échange, etc...

  3. #3
    Nouveau Candidat au Club
    Homme Profil pro
    Infographiste 3D et dév
    Inscrit en
    Avril 2016
    Messages
    6
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 41
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Infographiste 3D et dév

    Informations forums :
    Inscription : Avril 2016
    Messages : 6
    Points : 1
    Points
    1
    Par défaut
    Je peux comprendre la difficulté à voir où je veux en venir, je vais expliquer plus en détail ce que je veux faire.
    Je suis partis d'un serveur/client TCP que j'ai fais ( avant ce projet ) avec l'utilisation de forks, tous marche nickel. Le problème c'est que je dois passer par une liaison série RS232 pour faire bouger un bras robot et le soucis c'est le risque d'envois simultané de trames et donc soit une déconnexion d'un client ou bien une perte de trames.

    J'ai un client qui fait bouger le robot et un autre qui fait une visualisation en 3D de la position avec Unity. Pendant qu'un client envois des trames de déplacements l'autre client demande aussi au robot sa position.

    J'ai essayé d'adapter ce serveur avec des threads pour éviter des pertes de trames, un thread qui sera utilisé pour le déplacement et un autre pour la visualisation.

    j’espère que je suis compréhensible dans ce que je cherche à faire

  4. #4
    Expert confirmé
    Inscrit en
    Mars 2005
    Messages
    1 431
    Détails du profil
    Informations forums :
    Inscription : Mars 2005
    Messages : 1 431
    Points : 4 182
    Points
    4 182
    Par défaut
    C'est déjà plus clair mais ça ne change pas le fait que tu dois faire fonctionner chaque module séparément ou tu ne t'en sortiras pas.

    Je n'ai pas compris le truc avec le risque d'envois simultané / perte de trames, quel est le protocole de la liaison avec le bras ? Si c'est TCP alors il n'y a aucun risque, l'intégrité des informations transmises étant garantie par le protocole. La perte de connexion, elle, devra de toute manière être prise en charge, comme pour n'importe quelle application réseau.

    À ta place j'essaierais en premier lieu de me passer de threads en utilisant un socket non bloquant pour la réception des commandes, quitte à faire évoluer le programme ensuite si le résultat n'est pas satisfaisant.

  5. #5
    Nouveau Candidat au Club
    Homme Profil pro
    Infographiste 3D et dév
    Inscrit en
    Avril 2016
    Messages
    6
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 41
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Infographiste 3D et dév

    Informations forums :
    Inscription : Avril 2016
    Messages : 6
    Points : 1
    Points
    1
    Par défaut
    C'est une liaison série, on ne peut faire passer qu'une demande à la fois. Je me penchais sur les threads pour la gestion de mutex.

  6. #6
    Expert confirmé
    Inscrit en
    Mars 2005
    Messages
    1 431
    Détails du profil
    Informations forums :
    Inscription : Mars 2005
    Messages : 1 431
    Points : 4 182
    Points
    4 182
    Par défaut
    Une connexion TCP fonctionne sur la couche de transport toujours de la même manière ; la couche matérielle peut bien être un câble Ethernet, une liaison série ou des signaux de fumée.

    On ne se comprend pas : peux-tu récapituler les protagonistes, technologies et contraintes de communication en jeu ? Pour l'instant j'ai cela :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    +--------------+         +---------+         +--------------+
    |  Controller  | <-----> |  Robot  | ------> |  Visualizer  |
    +--------------+         +---------+         +--------------+

  7. #7
    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 967
    Points
    32 967
    Billets dans le blog
    4
    Par défaut
    Citation Envoyé par xanatus Voir le message
    Je me penchais sur les threads pour la gestion de mutex.
    Normalement on se penche sur les mutex pour la gestion des threads
    Tu ajoutes des threads pour pouvoir utiliser des mutex ?
    Sinon je plussoye le mode non-bloquant. Surtout si tu n'as aucune logique à faire/faisable dans un thread à part sur le buffer reçu.
    Tu ne risques rien d'envoyer simultannément en TCP, il gère tout ça en interne avec une jolie file d'attente etc.
    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.

  8. #8
    Nouveau Candidat au Club
    Homme Profil pro
    Infographiste 3D et dév
    Inscrit en
    Avril 2016
    Messages
    6
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 41
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Infographiste 3D et dév

    Informations forums :
    Inscription : Avril 2016
    Messages : 6
    Points : 1
    Points
    1
    Par défaut
    @Matt:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
     
    1 - Client "Bouger Robot" --------
                                                       |------> Raspberry (Serveur TCP) ----------------------> Robot
    2 - Client "Visualisation"  --------                                                            RS 232

    Client "Visualisation" : Envoi un "r" (toutes les secondes, peut être modifié) et attend en retour la position du robot.
    Client "Bouger Robot" : Envoi un ordre pour bouger le robot.

    @Bousk
    Je crois bien que je suis en train de tous mélanger

    Mon problème est que si l'envoi d'un "r" (Client 2) essaie de passer en même temps qu'une demande pour bouger le robot (client 1), le client 2 se fait jeter mais pas le client 1 qui reste fonctionnel.

    C'est pourquoi je pensais à l'utilisation de thread, déjà pour dispatcher les demandes serveur propre à chaque client.
    Puis en fouillant j'ai vu que grâce au mutex, on peut faire un switch entre deux demande.


    En tous cas merci à vous de prendre du temps pour m'aider.

    Voici les codes fonctionnels qui marche, avec des forks.

    Serveur:
    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
    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
    // nom: serveurMinimumTcp.c
    // description : recoit une lettre d'un client et envoie la lettre suivante dans l'alphabet
     
    #include <stdio.h>			// perror()
    #include <netinet/in.h>		// sockaddr_in et INADDR_ANY
    #include <sys/socket.h>		// socket(), bind(), recv(), send()
    #include <sys/types.h>		// socket(), bind(), recv(), send(), wait()
    #include <string.h>			// memset()
    #include <signal.h>			// signal()
    #include <wait.h>			// wait()
    // Pour open()
    #include <sys/types.h>
    #include <sys/stat.h>
    #include <fcntl.h>
     
    /* definition des constantes symboliques */
    #define TAILLE_FILE_DATTENTE_CONNEXION 5 // taille de la file d'attente de demandes de connexion
    #define NUMERO_PORT_SERVEUR 30000
     
    /* routine d'interception du signal SIGCHLD emis par les fils */
    void intercepterSignalSIGCHLD(int numeroSignalRecu);
     
    /* prototypes des fonctions */
    int communiquerAvecLeClient(int socketConnectee);
     
    int main()
    {
    	/* couple adresse IP, numero de port serveur */
    	struct sockaddr_in coupleAdresseIPNumeroPortServeur;
     
    	/* couple adresse IP, numero de port client */
    	struct sockaddr_in coupleAdresseIPNumeroPortClient;
    	int socketDemandeDeConnexion; /* socket permettant d'effectuer une demande de connexion */
    	int socketConnectee; /* socket dediee a une connexion etablie */
    	socklen_t longueurClient; /* longueur du couple adresse IP, numero de port du client */
    	int optval;
     
    	/* Mise en place de l'interception de SIGCHLD emis par les fils */
    	signal(SIGCHLD,intercepterSignalSIGCHLD);
     
    	/* Initialiser les structures a des octets de valeurs */
    	memset(&coupleAdresseIPNumeroPortServeur,0,sizeof(struct sockaddr_in));
    	memset(&coupleAdresseIPNumeroPortClient,0,sizeof(struct sockaddr_in));
     
    	/* Creer la socket serveur en mode TCP */
    	if ((socketDemandeDeConnexion = socket(PF_INET,SOCK_STREAM,0)) < 0)
    		{
    			perror("socket");
    			return -1;
    		}
     
    	// Reutiliser le meme port en cas d'interruption brutal du serveur
    	optval = 1;
    	setsockopt(socketDemandeDeConnexion, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof optval);
     
    	/* Associer la socket serveur a un couple adresse IP, numero de port */
    	/* socket du domaine reseau */
    	coupleAdresseIPNumeroPortServeur.sin_family = AF_INET;
     
    	/* INADDR_ANY pour accepter les requetes de toutes les interfaces reseau du serveur */
    	coupleAdresseIPNumeroPortServeur.sin_addr.s_addr = htonl(INADDR_ANY);
     
    	/* affecter le numero de pott de l'application */
    	coupleAdresseIPNumeroPortServeur.sin_port = htons(NUMERO_PORT_SERVEUR);
    	if (bind(socketDemandeDeConnexion,(struct sockaddr *)(&coupleAdresseIPNumeroPortServeur),sizeof(struct sockaddr_in)) < 0)
    	{
    		perror("bind");
    		return -1;
    	}
     
    	/* Creer une file d'attente de demandes de connexion */
    	if (listen(socketDemandeDeConnexion,TAILLE_FILE_DATTENTE_CONNEXION) <0)
    	{
    		perror("listen");
    		return -1;
    	}
    	longueurClient = sizeof(struct sockaddr_in);
     
    	while(1)
    	{
    		/* Attendre une demande de connexion du client */
    		socketConnectee = accept(socketDemandeDeConnexion,(struct sockaddr *)(&coupleAdresseIPNumeroPortClient),&longueurClient);
    		if (socketConnectee < 0)
    		{
    			perror ("accept");
    		}
    		else
    		{
    			/* Creer un fils pour traiter la communication */
    			if (fork() == 0)
    			{
    				/* Dans le fils */
    				/* Fermer dans le fils la socket serveur permettant d'effectuer la demande */
    				if (close(socketDemandeDeConnexion)<0)
    				{
    					perror("close");
    					return -1;
    				}
     
    				// Envois des requêtes
    				while(1)
    				{
    					if (communiquerAvecLeClient(socketConnectee) < 0 )
    					{
    						fprintf(stderr,"communiquerAverLeClient : echec\n");
    						close(socketConnectee);
    						return -1;
    					}
    					printf("Boucle ---> %d\n", i++);
    				}
     
    				printf("Close connection\n");
     
    				/* Fermer dans le fils la socket connectee */
    				if (close(socketConnectee) < 0)
    				{
    					perror("close");
    					return -1;
    				}
    				return 0;
    			}
    			/* dans le pere */
    			/* fermer dans le pere la socket connectee */
    			if(close(socketConnectee) < 0)
    			{
    				perror("close");
    				return -1;
    			}
    		}
    	}
    	return 0;
    }
     
    void intercepterSignalSIGCHLD(int numeroSignalRecu)
    {
    	wait(NULL);
    }
     
    int communiquerAvecLeClient(int socketConnectee)
    {
    	/* Déclaration des Variables */
    	char trameFinale[14];
    	char reponseTrame[14];
    	int nbOctetsRecus = 0;
    	int nbOctetsARecevoir = 2;
        int nbOctetTotal = 0;
    	int fd;
    	int chemin;
    	int nbOctetsEcrits;
        char L = 'L';
     
    	/* Recevoir la requête */
    	if ((nbOctetsRecus=recv(socketConnectee, trameFinale, sizeof(trameFinale), 0)) == -1)
    	{
    		perror("recvfrom");
    		return -1;
    	}
     
    	printf("nbOctetsRecus depuis le reseau : %d\n",nbOctetsRecus );
    	chemin = open("/dev/ttyUSB0", O_RDWR);
     
    	if(chemin < 0)
    	{
    		perror("open");
    		return -1;
    	}
     
    	nbOctetsEcrits = write(chemin, &L, 1);
     
        if (nbOctetsEcrits < 0)
    	{
    		perror("write");
    		close(chemin);
    		return -1;
    	}
     
    	usleep(40000);
     
    	nbOctetsEcrits = write(chemin, trameFinale, nbOctetsRecus);
     
    	if (nbOctetsEcrits < 0)
    	{
    		perror("write");
    		close(chemin);
    		return -1;
    	}
     
    	usleep(40000);
     
    	/* Retour Trame informations 'r' */
        if (trameFinale[0] == 'r')
        	nbOctetsARecevoir = 11;
     
    	nbOctetsRecus = read(chemin, &reponseTrame[0], nbOctetsARecevoir);
     
        if (nbOctetsRecus < 0)
        {
        	perror("read");
        	close(chemin);
        	return -1;
        }
     
    	printf("nbOctetsRecus: %d, nbOctetsARecevoir:%d,\n ", nbOctetsRecus, nbOctetsARecevoir);
     
    	// Visualisation des trames
    	int i = 0;
    	for ( i ; i < nbOctetsRecus ; i++ )
    	    printf(":%02x:", reponseTrame[i]);
     
    	// Envois et fermeture
    	send(socketConnectee, reponseTrame, nbOctetsRecus, 0);
    	close(chemin);
    	return 0;
    }

    Client:
    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
    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
    // clientMinimumTcp.c
    // envoie une lettre au serveur et reçoit la lettre suivante dans l'alphabet
    // clientMinimumTcp <adresse_IP_serveur>
    // clientMinimumTcp 192.168.10.1
     
    #include <stdio.h>			// perror(), printf(), fprintf(), scanf() 
    #include <arpa/inet.h>		// inet_pton(), htons()
    #include <sys/socket.h>		// socket(), recv(), send(), connect()
    #include <sys/types.h>		// socket(), recv(), send(), connect()
    #include <string.h>			// memset() 
     
    #define NUMERO_PORT_SERVEUR 30000
     
    int CloseConnection(int socket);
     
    int main(int argc, char * argv[])
    {
    	/* couple adresse IP, numero de port serveur */
    	struct sockaddr_in coupleAdresseIPNumeroPortServeur;
    	int socketClient; /* socket client */
    	int longueurReponse; /* longueur de la reponse recue */
    	socklen_t longueurServeur; /* longueur du couple adresse IP, numero de port	du serveur */
    	long int adresseIPServeur; /* adresse IP du Serveur */
    	char lettre, lettreReponse;
     
    	/* Tester le nombre d'arguments */
    	if (argc != 2)
    	{
    		fprintf(stderr,"usage : clientMinimumTcp <adresse_IP_serveur>\nexemple : clientMinimumTcp 192.168.10.1\n");
    		return -1;
    	}
     
    	/* Initialiser les structures a des octets de valeurs */
    	memset(&coupleAdresseIPNumeroPortServeur,0,sizeof(struct sockaddr_in));
     
    	/* Initialiser l'adresse IP et le numero de port du serveur */
    	/* adresse IP au format reseau */
    	if (inet_pton(AF_INET, argv[1], (void *)&adresseIPServeur) < 0)
    	{
    		perror("inet_pton");
    		return -1;
    	}
     
    	/* socket du domaine reseau */
    	coupleAdresseIPNumeroPortServeur.sin_family = AF_INET;
     
    	/* adresse IP */
    	coupleAdresseIPNumeroPortServeur.sin_addr.s_addr = adresseIPServeur;
     
    	/* affecter le numero de port de l'application */
    	coupleAdresseIPNumeroPortServeur.sin_port = htons(NUMERO_PORT_SERVEUR);
     
    	/* Creer la socket client en mode TCP */
    	if ((socketClient = socket(PF_INET,SOCK_STREAM,0)) < 0)
    	{
    		perror("socket");
    		return -1;
    	}
     
    	/* Effectuer une demande de connexion */
    	longueurServeur = sizeof(struct sockaddr_in);
    	if (connect(socketClient, (struct sockaddr *)&coupleAdresseIPNumeroPortServeur,	longueurServeur) < 0)
    	{
    		perror("connect");
    		return -1;
    	}
     
     
    	/****************************************************************************/
     
    	// Varibales
    	int choix = 0;
    	int choixCase = 0;
    	int i = 1;
    	unsigned char trameFinale[14];
    	char chaineAxe[2];
    	int valeur;
    	char chaineEspace[5] = {0x20,0}; 
    	char chaineEnvoi[5] = {0x0A,0};
    	char reponseTrame[14] = {0};
    	int nbOctetsRecus = 0;
     
    	// Requêtes des mouvements du robot
    	printf("\n");
    	printf("*******************************************************\n");
    	printf("*                Client Hercules 2000                 *\n");
    	printf("*******************************************************\n");
    	printf("\n");
    	printf("Que voulez-vous faire:\n");
    	printf("\n");
    	printf("	1 - Déplacer le robot.\n");
    	printf("	2 - Quitter le client.\n");
    	printf("	Choix: ");
    	scanf("%d", &choix);
     
    	switch(choix)
    	{
    		case 1:
    			/* CHOIX DE L'AXE */
    			do
    			{
    				printf("\n");
    				printf("*******************************************************\n");
    				printf("*               Deplacements du Robot                 *\n");
    				printf("*******************************************************\n");
    				printf("\n");
    				do
    				{
    					printf("---- Veuillez entrer l'Axe a deplacer ( B / C / E / P / T / R ) ---- \n");
    					printf("-->  ");
    					scanf("%s",chaineAxe);
    					if (chaineAxe[0]!='B' && chaineAxe[0]!='C' && chaineAxe[0]!='E' && chaineAxe[0]!='P' && chaineAxe[0]!='T' && chaineAxe[0]!='R' && chaineAxe[0]!='r')
    						printf("*** Erreur de saisie, veuillez recommencer ***\n\n");
    				} while(chaineAxe[0]!='B' && chaineAxe[0]!='C' && chaineAxe[0]!='E' && chaineAxe[0]!='P' && chaineAxe[0]!='T' && chaineAxe[0]!='R' && chaineAxe[0]!='r');
    				printf("\n");
    				if (chaineAxe[0] == 'r')
    					sprintf(trameFinale,"%c",chaineAxe[0]);
    				else
    				{
    					printf("---- Veuillez entrer la Valeur du déplacement ----\n");
    					printf("-->  ");
    					scanf("%d",&valeur);
    					valeur = (1024 * valeur)/320;
    					sprintf(trameFinale,"%s%d.0:10%s%s",chaineAxe,valeur,chaineEspace,chaineEnvoi);
    				}
    				fgetc(stdin);
     
    				/* Envoyer la requete */
    				if (send(socketClient, trameFinale, strlen(trameFinale), 0) == -1)
    				{perror("sendto");return -1;}
     
    				/* Recevoir la reponse */
    				if ((nbOctetsRecus = recv(socketClient,reponseTrame, sizeof(reponseTrame),0)) == -1)
    				{perror("recvfrom");return -1;}
     
    				// Menu
    				printf("\n");
    				printf("1 - Recommencer\n");
    				printf("2 - Quitter\n");
    				printf("Choix: ");
    				scanf("%d", &choixCase);
    				if(choixCase == 2)
    					CloseConnection(socketClient);
    			}while(choixCase == 1);
    			break;
    		case 2:
    			CloseConnection(socketClient);
    			break;
    	}
    }
     
     
    // Fermeture de la connection
    int CloseConnection(int socket)
    {
    	printf("\n");
    	printf("Au revoir :)\n");
    	if (close(socket) < 0)
    	{
    		perror("close");
    		return -1;
    	}
    	return 0;
    }

  9. #9
    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 967
    Points
    32 967
    Billets dans le blog
    4
    Par défaut
    Que l'on soit clair : un mutex c'est juste un objet que l'on ne peut lock qu'une seule fois dans l'application.
    C'est juste un truc pratique pour créer un point de contention. Dispatch, switch ou quoi que ce soit n'est décidé que par le programme.

    Tout ça pour dire, j'ai toujours pas compris ton problème. T'as un serveur (le robot ?), t'as 2 clients, et ton serveur ne peut faire qu'une action à la fois ?
    Ben.. je dirais qu'on est en présence d'une bête machine à état et file d'attente des actions des clients ou rejets de celle-ci.
    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.

  10. #10
    Nouveau Candidat au Club
    Homme Profil pro
    Infographiste 3D et dév
    Inscrit en
    Avril 2016
    Messages
    6
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 41
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Infographiste 3D et dév

    Informations forums :
    Inscription : Avril 2016
    Messages : 6
    Points : 1
    Points
    1
    Par défaut
    Le serveur envoi des ordres au boîtier du robot qui le fait bouger, le serveur peut recevoir plusieurs ordres en même temps mais pas le boîtier robot.

    Faut que j'arrive a effectivement gérer une file d'attente:
    - Quand le client "bouger" envois une commande, le client "visualiser" arrête d'envoyer des "r" et une fois la commande passer les "r" reprennent.

    "r" est une commande qui demande au boîtier robot de renvoyer sa position.

  11. #11
    Expert confirmé
    Inscrit en
    Mars 2005
    Messages
    1 431
    Détails du profil
    Informations forums :
    Inscription : Mars 2005
    Messages : 1 431
    Points : 4 182
    Points
    4 182
    Par défaut
    Ok, au final tu t'imagines la solution beaucoup plus compliquée qu'elle ne l'est.

    Ton robot n'a qu'un seul interlocuteur : le Raspberry Pi. Tout ce que ce dernier a à faire c'est :
    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
    tant que le programme tourne faire
         recevoir des commandes (TCP, non bloquant)
         
         pour chaque commande reçue faire
             envoyer la commande au robot (RS232)
         finpour
         
         si des clients sont connectés pour la visualisation alors
             demander sa position au robot (RS232)
             
             pour chaque client faire
                 envoyer les données reçues (TCP)
             finpour
         finsi
    fin tant que
    Nul besoin de paralléliser quoi que ce soit.

  12. #12
    Nouveau Candidat au Club
    Homme Profil pro
    Infographiste 3D et dév
    Inscrit en
    Avril 2016
    Messages
    6
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 41
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Infographiste 3D et dév

    Informations forums :
    Inscription : Avril 2016
    Messages : 6
    Points : 1
    Points
    1
    Par défaut
    J'ai réussi à me faire comprendre

    Si je lis ce que tu as mis, il faudrait que chaque client soit identifié par le serveur ou bien les demandes en elle-même?
    Comment faire? Un client peut envoyer un ID au serveur pour se faire reconnaître?

    Mmmmh, il faudrait que ce soit le serveur lui même qui envois les "r" au robot et non client---->serveur---->robot, cela devrait être plus simple à gérer?

  13. #13
    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 967
    Points
    32 967
    Billets dans le blog
    4
    Par défaut
    Chaque client a déjà un identifiant unique que tu peux utiliser : la socket.
    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.

  14. #14
    Expert confirmé
    Inscrit en
    Mars 2005
    Messages
    1 431
    Détails du profil
    Informations forums :
    Inscription : Mars 2005
    Messages : 1 431
    Points : 4 182
    Points
    4 182
    Par défaut
    Je vois au moins les avantages suivants à ne pas relayer des commandes depuis le client de visualisation (ce que tu nommes « r ») vers le robot :

    • il faudrait que le serveur relaie ces commandes, son code et celui du client de visualisation en seraient plus complexes ;
    • à quoi bon puisque le client ne fait que visualiser ? C'est pas vraiment interactif, on se doute bien qu'il va demander la position du robot toutes les T millisecondes et on peut prendre les devants ;
    • la communication est donc unidirectionnelle au final (on pourrait même envoyer via UDP, vu qu'on s'en fiche un peu si tous les paquets n'arrivent pas) ;
    • sous réserve que tu normalises les données de position du robot dans un format à toi avant de les envoyer, cela permet de s'abstraire du protocole de communication avec le robot : si tu changes de robot et de protocole plus tard tu n'auras à mettre à jour ou remplacer que le module de liaison RS232 côté serveur, pour le client c'est transparent ;
    • cela permet la prise en charge de plusieurs clients de visualisation : on récupère les données du robot une seule fois pour chaque frame et on les dispatche à tous les clients.


    D'ailleurs idéalement il faudrait la même couche d'abstraction côté commandes. Le client contrôleur enverrait quelque chose du style de « TURN CCW 115° » et le serveur traduirait au robot.

Discussions similaires

  1. Réponses: 2
    Dernier message: 31/10/2015, 03h13
  2. Soucis avec I/O tcp + thread
    Par Bluespear dans le forum Entrée/Sortie
    Réponses: 4
    Dernier message: 13/10/2008, 17h53
  3. client/server tcp, probleme socket
    Par Misaki dans le forum Programmation et administration système
    Réponses: 1
    Dernier message: 20/12/2007, 09h46
  4. Réponses: 4
    Dernier message: 16/03/2007, 10h07
  5. Sockets TCP/ Threads
    Par guillaume16 dans le forum C++
    Réponses: 3
    Dernier message: 27/07/2006, 23h45

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