Un petit réseau local en C

Discutez d'informatique ici !
Avatar de l’utilisateur
Rockleader
Habitué(e)
Messages: 2126
Enregistré le: 11 Oct 2011, 19:42

Un petit réseau local en C

par Rockleader » 10 Jan 2015, 16:59

Bonsoir à tous,

la nouvelle année est là et les bonnes résolution aussi :ptdr: Enfin on essaie de s'y tenir quoi :marteau:


bref, j'ai décidé d'essayer de combler une de mes lacunes les plus importantes; mais je ne sais pas vraiment par où commencer et j'espérais pouvoir être guidé un petit peu.

Concrètement, ce qui m'intéresse ce n'est pas le code en lui même; je trouve pleins de code de serveur et de client sur le net..mais sans explication c'est dur à comprendre...et de toute façon je ne veux pas réutiliser du code je veux le faire moi même pour pouvoir comprendre.


Donc, pour commencer, je voudrais réaliser deux programmes simples.


un serveur.c et client.c

Le client envoie un message au serveur.
Le serveur affiche le message.

Que dis-je ??? Un message ? non simplifions encore plus..un simple caractère !




Il serait bien sûr possible de réaliser ça, sans passer par un modèle client serveur, peut être en utilisant des thread ou des signaux...mais là c'est un autre problème, je veux vraiment me concentrer sur le modèle client serveur.


Pour le moment je me consacre sur le serveur...plus particulièrement la création du serveur

Si j'ai bien compris on va devoir utiliser des socket.

Et donc des numéros de ports et d'adresse IP.

Je crois aussi avoir compris que les numéros de ports < 1024 sont utilisés par le système.

Donc première question: Comment choisir un numéro de port ?

On a ensuite besoin de l'adresse Ip j'imagine ? Ip c'est niveau réseau. Mais dois je utiliser l’adresse IP de mon ordi ou de ma box ?
De plus, y a t'il une fonction permettant de récupérer cette ip ? Parce que si je me trompe pas les adresse IP changent à chaque reboot de la box et sont donné par les opérateurs en france de façon aléatoire, donc je peux pas juste mettre mon ip actuelle.


Une fois que l'on a déterminé le numéro de port et l'adresse IP; a t'on besoin d'autre chose comme donnée pour créer notre serveur ? Si oui les quelles ?


Si non, comment on fait concrètement d'un point de vue du code pour créer ce serveur.

J'ai lu que le prototype de la socket était le suivant..mais je m'imagine bien que ça n'est pas aussi simple que de remplir une socket.

Code: Tout sélectionner
int socket(int domain, int type, int protocol);



Merci à vous pour votre aide.




PS: Je n'ai pas spécifié sur quel OS; il y a des différences entre windows et linux il me semble pour les socket...mais je devrais m'en sortir sur ce point là..je veux surtout comprendre d'un point de vue algo comment ça s'écrit.

PS2: Je suis également preneur de tout site expliquant ce sujet là...en français si possible pour le coup^^ J'en ai visité pas mal, mais soit ils ne donnent que du code, soit ils donnent trop de théorie et pas assez de code....
Cette histoire est entièrement vraie puisque je l'ai inventé du début à la fin !



Cliffe
Membre Rationnel
Messages: 967
Enregistré le: 12 Juin 2012, 14:25

par Cliffe » 10 Jan 2015, 21:44


Avatar de l’utilisateur
Rockleader
Habitué(e)
Messages: 2126
Enregistré le: 11 Oct 2011, 19:42

par Rockleader » 10 Jan 2015, 22:10




Merci pour ta réponse...mais rien que le fait de rajouter l'interface graphique à tout ça suffit à me perdre...je cherche à faire un truc le plus minimaliste possible en console pour comprendre le fonctionnement.
Cette histoire est entièrement vraie puisque je l'ai inventé du début à la fin !

Avatar de l’utilisateur
fatal_error
Modérateur
Messages: 6610
Enregistré le: 22 Nov 2007, 13:00

par fatal_error » 10 Jan 2015, 22:15

google. socket C :/
http://broux.developpez.com/articles/c/sockets/

Donc première question: Comment choisir un numéro de port ?

1337.

On a ensuite besoin de l'adresse Ip j'imagine ? Ip c'est niveau réseau. Mais dois je utiliser l’adresse IP de mon ordi ou de ma box ?

quelle différence? Si tu targettes ton serveur à l'adresse 192.168.0.1, c'est sur que si t'es à carrouf tu pourras pas te connecter au serveur.
Si t'es chez toi sur ton wifi, ben très probablement t'y auras accès.

Si tu mets l'adresse de ta box, la box saura pas à qui associer le flot de donné (il faut rediriger les données du port 1337 vers 192.168.0.1 si ton serveur est sur la machine à l'ip 192.168.0.1)

De plus, y a t'il une fonction permettant de récupérer cette ip ?

ifconfig.
Parce que si je me trompe pas les adresse IP changent à chaque reboot de la box et sont donné par les opérateurs en france de façon aléatoire, donc je peux pas juste mettre mon ip actuelle.

ca dépend des opérateurs, free permet l'ip fixe par exemple.

mais de toute facon, pour construire ton serveur tu as juste besoin normalement, de définir les ip que tu autorises (et t'as qu'à toutes les autoriser si tu testes), mais à partir du moment où t'es connecté à la box c'est bon...
la vie est une fête :)

Avatar de l’utilisateur
Rockleader
Habitué(e)
Messages: 2126
Enregistré le: 11 Oct 2011, 19:42

par Rockleader » 11 Jan 2015, 13:46

Bon, je vais essayer de reprendre le code suivant en rajoutant des commentaires, ça me parait plus simple pour comprendre les étapes.
Il s'agissait d'un code fournit dans un projet que je vais tenter de décortiquer.
Code: Tout sélectionner
int main(int argc, char *argv[])
{
     int sockfd, newsockfd, portno, clilen;
     char buffer;
     struct sockaddr_in serv_addr, cli_addr;
[COLOR=Red]//serv_addr et cli_addr seront deux variables structuré servant à paramétrer le client et le serveur[/COLOR]
     int n;

     sockfd = socket(AF_INET, SOCK_STREAM, 0);
     if (sockfd -1);
[COLOR=Red]//Tant que l'on a pas lu toute la socket on l'écrit...mais je ne comprends pas, on l'écrit dans le même buffer que l'on lit non ? Sa va pas faire une boucle infini ça ?[/COLOR]
     return 0;
}



Quelques question, pourquoi doit on lier, puis accepter ? Ne pourrait on pas penser que si on accepte de lier une connexion on a aucune raison de ne pas l'accepter par la suite ?

J'ai bien d'autres questions en tête, mais je vais attendre de voir vos réponses sur ce que j'ai écris ici.
Cette histoire est entièrement vraie puisque je l'ai inventé du début à la fin !

Avatar de l’utilisateur
fatal_error
Modérateur
Messages: 6610
Enregistré le: 22 Nov 2007, 13:00

par fatal_error » 11 Jan 2015, 14:39

donc d'abord, pourquoi lier ca veut rien dire.
c'est pourquoi lier quoi et quoi.

et non tu lies pas une socket et une connexion..
tu lies une socket et une adresse.
si tu regardes ce que c'est une adresse, c'est pour ton exemple
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = INADDR_ANY;
serv_addr.sin_port = htons(portno);
idem, le port 9000, n'importe quel IP de type AF_INET.

maintenant, ca veut pas dire que tu recois des données...
ca veut juste dire t'as créé une socket qui >peut< recevoir des données sur le port 9000 provenant de n'importe quelle IP.
mais elle est pas encore prete..

apres tu listen...donc là je présume, tu rates toutes les connexions qui arrivent... et au bout d'un moment t'as la backlog qui est pleine et le client se prend des ECONNREFUSED

ensuite, tu accept, et donc là tu vas décrémenter la backlog..(ou ptet que c'est fait quand tu closes la socket...) et gérer ta connexion.

Enfin, à ce niveau là, tu lis le man... il est pas mal rédigé..

et enfin, relis http://broux.developpez.com/articles/c/sockets/ c'est détaillé pas à pas et en français
la vie est une fête :)

Avatar de l’utilisateur
Rockleader
Habitué(e)
Messages: 2126
Enregistré le: 11 Oct 2011, 19:42

par Rockleader » 13 Jan 2015, 14:37

Bon, en bidouillant un peu


J'ai réussi à faire ce que je voulais.


Je crée mon serveur.
Mon client envoie un message au serveur.
*Problème ici mon serveur ne veut pas afficher le message reçu*
Le serveur effectue un traitement sur le message
Le serveur renvoi au client
Le client affiche le message obtenu

Sauf que, pour je ne sais quelle raison un affichage ne se fait pas coté serveur.


Coté serveur

Code: Tout sélectionner
#include
#include 
#include
#include
#include
#include
#include

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

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

     sockfd = socket(AF_INET, SOCK_STREAM, 0);
     if (sockfd -1);

     return 0;
}



Coté client

Code: Tout sélectionner
#include
#include
#include
#include
#include
#include
#include
#include 

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

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

    char *msg="Message d'exemple";

    portno = 7000;
    sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if (sockfd h_addr,
         (char *)&serv_addr.sin_addr.s_addr,
         server->h_length);
    serv_addr.sin_port = htons(portno);
    if (connect(sockfd,(struct sockaddr*)&serv_addr,sizeof(serv_addr)) < 0)
        error("ERROR connecting");

    char *buffer=malloc(sizeof(char)*(strlen(msg)+1));

    write(sockfd,msg,strlen(msg));
    read(sockfd,buffer,strlen(msg));

    // we receive an array of characters, so it must be converted
    // to a string by adding a \0 at the end
    buffer[strlen(msg)]='\0';
    printf("%s\n", buffer);

    return 0;
}
Cette histoire est entièrement vraie puisque je l'ai inventé du début à la fin !

Avatar de l’utilisateur
fatal_error
Modérateur
Messages: 6610
Enregistré le: 22 Nov 2007, 13:00

par fatal_error » 13 Jan 2015, 15:20

Code: Tout sélectionner
       printf("%c",buffer); //n'affiche rien

par
Code: Tout sélectionner
       printf("%c\n",buffer); //n'affiche rien

\n force le flush,
(idem afficher le buffer de printf sur la sortie console)

doit yavoir un truc en c pour forcer le flush (à part mettre un \n) genre fprintf(stdout, "machin") ou bien un autre truc, je te laisse faire tes recherches..
la vie est une fête :)

Avatar de l’utilisateur
ampholyte
Membre Transcendant
Messages: 3940
Enregistré le: 21 Juil 2012, 08:03

par ampholyte » 13 Jan 2015, 15:36

fatal_error a écrit:
Code: Tout sélectionner
       printf("%c",buffer); //n'affiche rien

par
Code: Tout sélectionner
       printf("%c\n",buffer); //n'affiche rien

\n force le flush,
(idem afficher le buffer de printf sur la sortie console)

doit yavoir un truc en c pour forcer le flush (à part mettre un \n) genre fprintf(stdout, "machin") ou bien un autre truc, je te laisse faire tes recherches..


Bonjour,

Tout simplement :

Code: Tout sélectionner
fputc(buffer, stdout); /* Autant éviter le parsage inutile du printf */
fflush(stdout);


Petit conseil sur le nom de tes variables, evite d'appeler buffer un char, car buffer fait souvent référence à une chaine de caractère. Cela peut être perturbant pour toi dans quelques temps ou pour une personne relisant ton code de voir un "buffer" pour récupérer un char.

Avatar de l’utilisateur
Rockleader
Habitué(e)
Messages: 2126
Enregistré le: 11 Oct 2011, 19:42

par Rockleader » 13 Jan 2015, 17:27

Oui j'ai récupéré le code d'un ancien projet et quand j'ai voulu faire ça...j'avais commencé à écrire buffer[k] ... lorsque je me suis rendu compte à la compilation que c'était en fait un char --'


Mais en fait le truc c'est que pour l'affichage si je rajoute le \n

En sortie de console au lieu d'avoir

"Message"

J'aurais

M
e
s
s
a
g
e

étant donné que je récupère char par char. Pour ça que je n'ai pas mis le \n

Ceci dit le problème venait bien de là....




Après avoir fait plusieurs série d'exécution j'ai aussi pu remarqué que parfois..je ne reçois qu'une partie du message

Donc si j'envoie Message D'exemple..il se peut que je reçoive Message D'exem

J'imagine que c'est du à une perte d'information mais je pensais que la probabilité d'erreur était suffisamment faible pour ne pas en rencontrer sur un exemple si petit...
Cette histoire est entièrement vraie puisque je l'ai inventé du début à la fin !

Avatar de l’utilisateur
ampholyte
Membre Transcendant
Messages: 3940
Enregistré le: 21 Juil 2012, 08:03

par ampholyte » 13 Jan 2015, 18:39

Rockleader a écrit:Oui j'ai récupéré le code d'un ancien projet et quand j'ai voulu faire ça...j'avais commencé à écrire buffer[k] ... lorsque je me suis rendu compte à la compilation que c'était en fait un char --'


Mais en fait le truc c'est que pour l'affichage si je rajoute le \n

En sortie de console au lieu d'avoir

"Message"

J'aurais

M
e
s
s
a
g
e

étant donné que je récupère char par char. Pour ça que je n'ai pas mis le \n


Le fflush oblige le kernel à "envoyer la purée" => Voici un exemple qui le prouve :

Sans fflush
Code: Tout sélectionner
#include
#include

int main(void) {
   
    fprintf(stdout, "MESSAGE1 ");
    fprintf(stderr, "MESSAGE2 ");
    fprintf(stdout, "MESSAGE3 ");
    fprintf(stderr, "MESSAGE4 ");
    fprintf(stdout, "MESSAGE5 ");
   
    return (0);
}


=> Voici ce que ça donne chez moi :
Code: Tout sélectionner
MESSAGE2 MESSAGE4 MESSAGE1 MESSAGE3 MESSAGE5


C'est perturbant hein :p

Avec des fflush maintenant :
Code: Tout sélectionner
#include
#include

int main(void) {
   
    fprintf(stdout, "MESSAGE1 ");
    fflush(stdout);

    fprintf(stderr, "MESSAGE2 ");
    fflush(stderr);

    fprintf(stdout, "MESSAGE3 ");
    fflush(stdout);

    fprintf(stderr, "MESSAGE4 ");
    fflush(stderr);

    fprintf(stdout, "MESSAGE5 ");
    fflush(stdout);
   
    return (0);
}


Résultat: MESSAGE1 MESSAGE2 MESSAGE3 MESSAGE4 MESSAGE5

Si tu veux un peu plus d'explication sur le pourquoi du comment, je te l'expliquerais ^^.

Avatar de l’utilisateur
Rockleader
Habitué(e)
Messages: 2126
Enregistré le: 11 Oct 2011, 19:42

par Rockleader » 13 Jan 2015, 19:11

Priorité donné aux erreurs ? Ce qui est logique après tout^^
Cette histoire est entièrement vraie puisque je l'ai inventé du début à la fin !

Avatar de l’utilisateur
ampholyte
Membre Transcendant
Messages: 3940
Enregistré le: 21 Juil 2012, 08:03

par ampholyte » 14 Jan 2015, 09:49

Logique je ne pense pas. Lorsque je suis tombé sur ce problème, le code contenait plusieurs dizaines de milliers de ligne, alors pour débuguer tu passes déjà un certain temps à comprendre pourquoi les messages ne s'affichent pas dans l'ordre.

De plus la priorité n'est absolument pas donnée aux erreurs.

Quand on écrit :

Code: Tout sélectionner
fprintf(stdout, "TOTO")


Le message TOTO est envoyé au kernel. Le kernel va alors stocker ce message dans un buffer qui lui est propre et va vérifier que la taille contenue dans son buffer est supérieur à une certaine taille (cela varie suivant les os).

Lorsque la taille du message dans le buffer arrive à cette taille max, alors le kernel va fflush le contenu de ce buffer sur le flux en question (stdout, stderr, dans un fichier, ...).

Sauf que la taille MAX de ces flux n'est pas forcément pareil. On a aura peut-être une taille max plus grande pour stdout que stderr.

Ces variables sont paramétrables quand on fouille un peu dans les sources du kernel (il suffit de modifier puis recompiler).

Donc bien fait attention à fflush ou à rajouter '\n' lorsque l'on écrit dans un flux.

joel76
Membre Relatif
Messages: 230
Enregistré le: 11 Fév 2013, 16:31

par joel76 » 14 Jan 2015, 10:37

Une petite parenthèse, j'avais toujours cru comprendre que stderr était immédiatement envoyé au terminal connecté alors que ce n'était pas le cas pour stdout, je m'étais trompé ou quoi ?

Avatar de l’utilisateur
fatal_error
Modérateur
Messages: 6610
Enregistré le: 22 Nov 2007, 13:00

par fatal_error » 14 Jan 2015, 10:50

hello,

yes, stderr instant output et stdout line buffered
http://linux.about.com/library/cmd/blcmdl3_setvbuf.htm
The standard error stream stderr is always unbuffered by default.

mais bon, un coquin peut changer le type en block buffered :D
la vie est une fête :)

Avatar de l’utilisateur
ampholyte
Membre Transcendant
Messages: 3940
Enregistré le: 21 Juil 2012, 08:03

par ampholyte » 14 Jan 2015, 10:51

joel76 a écrit:Une petite parenthèse, j'avais toujours cru comprendre que stderr était immédiatement envoyé au terminal connecté alors que ce n'était pas le cas pour stdout, je m'étais trompé ou quoi ?


Après quelques recherches, en effet il semblerait que stderr soit non bufferisé par défaut.

src : http://man7.org/linux/man-pages/man3/setbuf.3.html

Par contre il est possible de passer par un mode bufferisé sur stderr (ce qui devait être mon cas à l'époque).

Avatar de l’utilisateur
Rockleader
Habitué(e)
Messages: 2126
Enregistré le: 11 Oct 2011, 19:42

par Rockleader » 14 Jan 2015, 16:47

Bien saisi, je suis pas sûr de tout comprendre du coup, mais j'y ferais attention :)

Une autre question, sans vraiment de rapport avec le sujet initial mais bon...

Comment on fait pour faire une sorte de compteur ?

Par exemple, je lance le programme.
A un moment donné je lance un timer et lorsque ce timer arrive à un certain nombre de seconde une action en particulier se déroule.



Je sais de mémoire qu'il y a une fonction permettant d'obtenir le nombre de seconde écoulés depuis 1970 si je me rappelle bien; cette même fonction que l'on utilise pour générer des nombres aléatoires qui ne le sont en fait pas vraiment.

Mais je ne m'en suis jamais servi réellement pour faire des timer.

Hors je pense que dans ce type de truc client serveur, ça peut être sympas d'avoir un timer pour faire un timeout si le client est connecté sans rien faire trop longtemps ou un truc du genre.

Puis même dans d'autres applis =)
Cette histoire est entièrement vraie puisque je l'ai inventé du début à la fin !

Avatar de l’utilisateur
ampholyte
Membre Transcendant
Messages: 3940
Enregistré le: 21 Juil 2012, 08:03

par ampholyte » 14 Jan 2015, 17:32

Tu peux te tourner vers la fonction :
http://linux.die.net/man/2/time
Code: Tout sélectionner
time_t time(time_t *time);


Tu peux l'utiliser comme ceci :

Code: Tout sélectionner
time_t t = time(NULL);
cela te retourne le nombre de secondes depuis le 1970-01-01 00:00:00 +0000 (UTC).

Si tu as besoin de mesurer des durées plus faible que la seconde, tu peux regarder cette fonction :
http://linux.die.net/man/2/gettimeofday

Avatar de l’utilisateur
fatal_error
Modérateur
Messages: 6610
Enregistré le: 22 Nov 2007, 13:00

par fatal_error » 14 Jan 2015, 18:11

tu peux faire un thread avec un sleep
ou bien utiliser alarm en non bloquant
la vie est une fête :)

Avatar de l’utilisateur
ampholyte
Membre Transcendant
Messages: 3940
Enregistré le: 21 Juil 2012, 08:03

par ampholyte » 14 Jan 2015, 18:38

De mon côté j'utilise régulièrement côté client :

Code: Tout sélectionner
signal(SIGALRM, callback);
signal(SIGPIPE, callback);
alarm(30); /* 30 secondes */

 

Retourner vers ϟ Informatique

Qui est en ligne

Utilisateurs parcourant ce forum : Aucun utilisateur enregistré et 5 invités

Tu pars déja ?



Fais toi aider gratuitement sur Maths-forum !

Créé un compte en 1 minute et pose ta question dans le forum ;-)
Inscription gratuite

Identification

Pas encore inscrit ?

Ou identifiez-vous :

Inscription gratuite