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 :

Serveur Multiprocessus ET Multithread - Débutant en C


Sujet :

Réseau C

  1. #1
    Futur Membre du Club
    Homme Profil pro
    Technicien maintenance
    Inscrit en
    Janvier 2013
    Messages
    5
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Technicien maintenance
    Secteur : Administration - Collectivité locale

    Informations forums :
    Inscription : Janvier 2013
    Messages : 5
    Points : 9
    Points
    9
    Par défaut Serveur Multiprocessus ET Multithread - Débutant en C
    Bien le bonjour le forum.
    Ô, toi forum tout-puissant, connaitrait-tu quelqu'un dont la grandeur d'âme n’ai d'égal que mon incompétence ?

    Je t'explique, forum, le pourquoi de mon incantation spiritiste qui me fais venir a toi.
    Je débute en C, j'ai lu quelques bouquin, je travaille actuellement avec "C en action" de Yves Mettier.
    Je me suis donné comme exercice de faire un serveur générique TCPIP dont le but inavoué est évidement la domination du monde, mais avant ça, il faudrait que ça marche correctement.
    Je me suis donc lancé sur les traces des recettes "créer un serveur multithread" et "créer un serveur multiprocessus" du bouquin. (pour les curieux, les codes sources d'exemples sont disponibles sur le site de l'éditeur ---> http://www.editions-eni.fr/livres/.b...d6061a64fa.zip )
    Je tente donc vènheument de concilier ces deux codes, pour créer des processus avec fork(), qui utiliseront chacuns plusieurs threads.
    (Ouah, le mec mégalo !!! 5 phrases, 5 commençant par "je"...)

    Malheureusement, le résultat attendu n'est pas au rendez-vous.

    Si un grand gourou mystique du réseau/multiprocessus/threads pouvait jeter un oeil a mon torchon, je lui serais reconnaissant a vie. Mon âme lui appartiendrait, évidement.

    Je précise que je tiens a cette démarche de partir du code existant, de le modifier, et de comprendre mes erreurs de raisonnement, c'est comme cela que j'apprends le mieux.

    Mon torchon :
    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
    215
    216
    217
    218
    219
    220
    221
    222
    223
    224
    225
    226
    227
    228
    229
    230
    231
    232
    233
    234
    235
    236
    237
    238
    239
    240
    241
    242
    243
    244
    245
    246
    247
    248
    249
    250
    251
    252
    253
    254
    255
    256
    257
    258
    259
    260
    261
    262
    263
    264
    265
    266
    267
    268
    269
    270
    271
    272
    273
    274
    275
    276
    277
    278
    279
    280
    281
    282
    #include <stdarg.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <unistd.h>
    #include <sys/socket.h>
    #include <sys/types.h>
    #include <netinet/in.h>
    #include <pthread.h>
    #include <sys/wait.h>
    #include <errno.h>
    #include <fcntl.h>
     
    typedef struct
    {
      int exist;
      pthread_t tid;
      int socket_id;
    } client_t;
     
    #define DEBUG 1
     
    #ifdef DEBUG
    #define DEBUG_TEST 1
    #else
    #define DEBUG_TEST 0
    #endif
     
    #define debug_print(fmt, ...) \
            do { if (DEBUG_TEST) fprintf(stderr, "%s:%d:%s(): " fmt, __FILE__, \
                                    __LINE__, __func__, __VA_ARGS__); } while (0)
     
    #define NB_CONNECTIONS_PAR_THREAD 1
    #define NB_THREADS 2
    #define NUM_PORT_SERVER 2000
    #define NB_CONNECTIONS_PAR_PROCESSUS 3
     
     
     
     
    //#######################        client          #######################
    void * client (void *userdata)
    {
      client_t *client = userdata;
      char hello[] = "Ah! Bonjour, vous\n";
      int i;
     
      printf ("Nouveau thread\n");
     
    /*
     * boucle pour utiliser le même thread pour plusieurs connexions.
     */
      for (i = 0; i < NB_CONNECTIONS_PAR_THREAD; i++)
        {
    /* attente initialisation client->socket_id pour
     * communiquer avec le client.
     */   debug_print("client->socket_id %d\n",client->socket_id);
          while (client->socket_id == -1)
            sleep (1);
          write (client->socket_id, hello, strlen (hello));
          shutdown (client->socket_id, 2);
          close (client->socket_id);
          client->socket_id = -1;
        }
    /* une structure pour chaque thread.
     * La présence de client->exist == 1 indique son existence.
     */
      client->exist = 0;
      printf ("Fin du thread\n");
      return (NULL);
    }
     
     
    //#######################   create_new_thread    #######################
    int create_new_thread (int socket_id, int nombre_max_de_threads)
    {
      debug_print("Utilisation du socket_id %d dans create_new_thread\n",socket_id);
      client_t *thread_params;
      int i;
     
    /* Tableau de structures client_t avec un élément par thread.
     */
      if (NULL ==
          (thread_params = malloc (NB_THREADS * sizeof *thread_params)))
        {
          fprintf (stderr, "Problème avec malloc()\n");
          exit (EXIT_FAILURE);
        }
      for (i = 0; i < NB_THREADS; i++)
        {
          thread_params[i].exist = 0;
          thread_params[i].socket_id = -1;
        }
     
      while (1)
        {
          pthread_attr_t *thread_attributes;
          int client_id;
          int thread_id;
          if (-1 == (client_id = accept (socket_id, NULL, 0))
                  && (errno != EWOULDBLOCK))
                {
                  fprintf (stderr, "Erreur sur accept()\n");
                  exit (EXIT_FAILURE);
                }
          debug_print("Accept() OK: client_id %d pour socket_id %d\n",client_id, socket_id);
          thread_id = -1;
          while (thread_id == -1)
            {
    /* Cherche un thread oisif. */
              for (i = 0; i < NB_THREADS; i++)
                {
                  if ((thread_params[i].exist != 0)
                      && (thread_params[i].socket_id == -1))
                    {
                      thread_id = i;
                      thread_params[i].socket_id = client_id;
                      printf ("Thread %d\n", i);
                      break;
                    }
                }
              if (NB_THREADS == i)
                {
    /* Réutilisation des threads si possible.
     * Si tous les threads existants sont occupés,
     * nous créons si possible un nouveau thread.
     */
                  for (i = 0; i < NB_THREADS; i++)
                    {
                      if (thread_params[i].exist == 0)
                        {
    /* Création du nouveau thread. */
                          thread_id = i;
                          thread_params[i].socket_id = client_id;
                          debug_print("Utilisation du thread %d pour client_id %d\n",thread_id,client_id);
                          thread_params[i].exist = 1;
    /* Initialisation des attributs d'un futur thread
     * pour qu'il soit détaché. */
                          printf ("Thread %d\n", i);
                          if (NULL ==
                              (thread_attributes =
                               malloc (sizeof *thread_attributes)))
                            {
                              fprintf (stderr, "Problème avec malloc()\n");
                              exit (EXIT_FAILURE);
                            }
                          if (0 != pthread_attr_init (thread_attributes))
                            {
                              fprintf (stderr, "Problème avec "
                                       "pthread_attr_init()\n");
                              free (thread_attributes);
                              exit (EXIT_FAILURE);
                            }
                          if (0 != pthread_attr_setdetachstate (
                                              thread_attributes,
                                              PTHREAD_CREATE_DETACHED))
                            {
                              fprintf (stderr, "Problème avec "
                                       "pthread_attr_setdetachstate()\n");
                              free (thread_attributes);
                              exit (EXIT_FAILURE);
                            }
    /* Création du thread. */
                          if (0 !=
                              pthread_create (&(thread_params[i].tid),
                                              thread_attributes, client,
                                              &(thread_params[i])))
                            {
                              fprintf (stderr,
                                       "Problème avec pthread_create()\n");
                              free (thread_attributes);
                              exit (EXIT_FAILURE);
                            }
                            debug_print("Creation du thread %d pour client_id %d sur socket_id %d\n",thread_id,client_id, socket_id);
    /* Libération des ressources nécessaires pour les attributs
     * du thread. */
                          pthread_attr_destroy (thread_attributes);
                          break;
                        }
                    }
     
                }
    /* Si thread_id != -1, un thread a été trouvé ou créé et nous quittons.
     * Sinon, nous bouclons dans l'attente de la libération d'un thread.
     */
              if (thread_id == -1)
                sleep (1);
              else
                return (socket_id);
            }
        }
      exit (EXIT_SUCCESS);
    }
     
    //#######################   create_tcp_server    #######################
    int create_tcp_server (int port, int nb_max_clients)
    {
      int socket_id;
      int optval = 1;
      struct sockaddr_in sockname;
     
    /* Création de la socket. */
      if (-1 == (socket_id = socket (PF_INET, SOCK_STREAM, 0)))
        {
          fprintf (stderr, "Impossible de créer une socket\n");
          exit (EXIT_FAILURE);
        }
     
    /* Changement d'un paramètre de la socket pour permettre une
     * réutilisation immédiate après sa fermeture.
     */
      debug_print("Socket créé. socket_id: %d dans le PID %d\n",socket_id,getpid());
      setsockopt (socket_id, SOL_SOCKET, SO_REUSEADDR, &optval,
                  sizeof (int));
     
    /* Affectation d'une adresse. */
      memset ((char *) &sockname, 0, sizeof (struct sockaddr_in));
      sockname.sin_family = AF_INET;
      sockname.sin_port = htons (port);
      sockname.sin_addr.s_addr = htonl (INADDR_ANY);
     
      //debug_print("Socket ID: %d\n",socket_id);
     
      if (-1 ==
          (bind
           (socket_id, (struct sockaddr *) &sockname,
            sizeof (struct sockaddr_in))))
        {
          fprintf (stderr, "Erreur sur bind() dans le processus %d, avec l'erreure \"%s\"'\n",getpid(),strerror(errno));
          exit (EXIT_FAILURE);
        }
      debug_print("Bind() OK: socket_id %d\n",socket_id);
    /* Mise en écoute de la socket. */
      if (-1 == (listen (socket_id, nb_max_clients)))
        {
          fprintf (stderr, "Erreur sur listen()\n");
          exit (EXIT_FAILURE);
        }
      debug_print("listen() OK: socket_id %d\n",socket_id);
      socket_id = create_new_thread(socket_id, NB_CONNECTIONS_PAR_THREAD);
      return socket_id;
    }
     
    //#############################    main    #############################
    int main ()
    {
      int socket_id;
      int client_id;
      long socket_flags;
     
      socket_id = create_tcp_server (2000, 10);
      debug_print("create_tcp_server OK: socket_id %d\n",socket_id);
     
      fcntl (socket_id, F_GETFL, &socket_flags);
      socket_flags |= O_NONBLOCK;
      fcntl (socket_id, F_SETFL, socket_flags);
     
      while (1)
        {
          int pid;
     
          client_id = -1;
          while (-1 == client_id)
            {
              while (waitpid (-1, NULL, WNOHANG) > 0);
            }
     
          switch ((pid = fork ()))
            {
            case -1:
              fprintf (stderr, "Problème avec fork()\n");
              exit (EXIT_FAILURE);
            case 0:
              debug_print("PID créé. %d\n",getpid());
              debug_print("Client_ID. %d\n",client_id);
              create_new_thread(socket_id, NB_CONNECTIONS_PAR_THREAD);
            default:
              close (client_id);
            }
        }
      exit (EXIT_SUCCESS);
    }
    Le code compile, mais un "telnet 127.0.0.1 2000" ne fonctionne qu'une seule fois. Une deuxieme tentative reste sur une connexion acceptée qui n'avance plus... pas de nouveau thread/processus créé.

    Je bosse avec ubuntu, si ça as de l'importance, et je compile avec gcc -o generic_server generic_server.c -Wall -lpthread, mais clang -o generic_server generic_server.c -Wall -lpthread marche bien aussi.

    Cordialement, bien a toi, Ô forum omniscient.

  2. #2
    Modérateur
    Avatar de Obsidian
    Homme Profil pro
    Développeur en systèmes embarqués
    Inscrit en
    Septembre 2007
    Messages
    7 370
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 47
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Développeur en systèmes embarqués
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Septembre 2007
    Messages : 7 370
    Points : 23 625
    Points
    23 625
    Par défaut
    Bonsoir et bienvenue,

    Citation Envoyé par klmmlk Voir le message
    Bien le bonjour le forum.
    Ô, toi forum tout-puissant, connaitrait-tu quelqu'un dont la grandeur d'âme n’ai d'égal que mon incompétence ?
    Jolie entrée en matière ! :-)

    Je me suis donné comme exercice de faire un serveur générique TCPIP dont le but inavoué est évidement la domination du monde, mais avant ça, il faudrait que ça marche correctement.
    Effectivement, c'est la bonne recette…

    Je me suis donc lancé sur les traces des recettes "créer un serveur multithread" et "créer un serveur multiprocessus" du bouquin. (pour les curieux, les codes sources d'exemples sont disponibles sur le site de l'éditeur ---> http://www.editions-eni.fr/livres/.b...d6061a64fa.zip )
    Je tente donc vènheument de concilier ces deux codes, pour créer des processus avec fork(), qui utiliseront chacuns plusieurs threads.
    (Ouah, le mec mégalo !!! 5 phrases, 5 commençant par "je"...)

    Malheureusement, le résultat attendu n'est pas au rendez-vous.
    Il y a beaucoup d'erreurs. En plus mélanger à la fois les forks et les créations de threads, c'est se compliquer grandement la tâche pour rien.
    Déjà, tu mets un fork() à l'intérieur d'une boucle while(1) sans faire de break explicite, si bien que si ta boucle parvient à tourner, alors tes processus fils vont à leur tour engendrer des petits-fils et tu vas mettre ta machine à genoux en faisant exploser le nombre de processus.

    Et ce n'est pas tout, tu joues à la fois avec le TLS, avec les variables locales à l'intérieur des boucles qui deviennent indéfinies d'un tour à l'autre même dans un seul fil, avec l'héritage des descripteurs de fichiers au travers d'un fork et en plus, le pointeur « client » porte le même nom que la fonction dans laquelle il est défini et masque le nom de cette fonction alors que, par ailleurs, on y fait référence justement pour créer les nouveaux threads. Tout cela est extrêmement périlleux.

    En fait, le seul thread qui fonctionne est celui créé dans create_tcp_server(). La boucle principale en bas de la fonction main() est inopérante. Elle souffre de plusieurs tares mais je crois (il est tard et j'ai cessé d'explorer plus avant les méandres de ton code) que tu ne surveille pas la fin de ton processus fils : celui-ci engendre des threads puis se termine normalement en atteignant la fin de la fonction. Seulement, les threads font justement partie intégrante du processus en question, et disparaissent automatiquement si celui-ci prend fin.

    Si un grand gourou mystique du réseau/multiprocessus/threads pouvait jeter un oeil a mon torchon, je lui serais reconnaissant a vie. Mon âme lui appartiendrait, évidement.
    Attention, certains pourraient te prendre au mot…

    Je précise que je tiens a cette démarche de partir du code existant, de le modifier, et de comprendre mes erreurs de raisonnement, c'est comme cela que j'apprends le mieux.
    C'est généralement mieux qu'une page blanche en effet mais, dans le cas présent, ça engendre plus de confusion qu'autre chose. Il faudrait au contraire partir toi-même du plus simple et complexifier progressivement ton propre programme, en utilisant éventuellement plusieurs copies de ton code source pour suivre ta progression ou, mieux, utiliser un logiciel de versionning tel que git ou mercurial.

    Bon courage.

  3. #3
    Futur Membre du Club
    Homme Profil pro
    Technicien maintenance
    Inscrit en
    Janvier 2013
    Messages
    5
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Technicien maintenance
    Secteur : Administration - Collectivité locale

    Informations forums :
    Inscription : Janvier 2013
    Messages : 5
    Points : 9
    Points
    9
    Par défaut
    Citation Envoyé par Obsidian Voir le message
    Jolie entrée en matière ! :-)
    Merci ;D


    Citation Envoyé par Obsidian Voir le message
    Il y a beaucoup d'erreurs. En plus mélanger à la fois les forks et les créations de threads, c'est se compliquer grandement la tâche pour rien.
    Pour le nombre d'erreurs, c'est bien ce dont je me rends compte en me relisant chaque jour, je découvre de nouvelles absurdités régulièrement.
    Concernant les fork() threadés ensuite, c'est pour envisager la performance, les systèmes actuels etant souvent multi-coeurs, un monoprocessus ne les exploite pas. Et les sytèmes actuels étant également multithreads, un monothread ne les exploite pas non plus. d'ou l'idée de répartir sur plusieurs processus (X par coeur, donc) et de multithreader chacun ensuite (Y par processus, donc par coeur, du coup).

    C'est cette infographie qui m'a donné envie de cette architecture -> http://researcher.watson.ibm.com/res...oue_slides.pdf surtout pages 4 et 5.

    Citation Envoyé par Obsidian Voir le message
    Déjà, tu mets un fork() à l'intérieur d'une boucle while(1) sans faire de break explicite, si bien que si ta boucle parvient à tourner, alors tes processus fils vont à leur tour engendrer des petits-fils et tu vas mettre ta machine à genoux en faisant exploser le nombre de processus.
    Heuuuu... oui, certes! D'ailleurs, j'ai déjas fais des fork bombs avec ce code, sans vraiment le vouloir
    Mais... Il s'agit pourtant d'une portion de code éxistante. Aurais-je supprimé quelques instructions importantes ?
    Ceci est l'illustration multiprocessus m'ayant servi de base:
    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
    int
    create_tcp_server (int port, int nb_max_clients)
    {
    /* Voir le code dans la recette "Créer un serveur TCP/IP". */
    }
     
    int
    main ()
    {
      int socket_id;
      int client_id;
      char hello[] = "Bonjour, vous\n";
     
      socket_id = create_tcp_server (2000, 10);
      while (1)
        {
          int pid;
          if (-1 == (client_id = accept (socket_id, NULL, 0)))
            {
              fprintf (stderr, "Erreur sur accept()\n");
              exit (EXIT_FAILURE);
            }
          switch ((pid = fork ()))
            {
            case -1:
              fprintf (stderr, "Problème avec fork()\n");
              exit (EXIT_FAILURE);
            case 0:
              close (socket_id);
              write (client_id, hello, strlen (hello));
              shutdown (client_id, 2);
              close (client_id);
              exit (EXIT_SUCCESS);
            default:
              close (client_id);
              /* Balayette à zombie */
              while (waitpid (-1, NULL, WNOHANG) > 0);
            }
        }
      exit (EXIT_SUCCESS);
    }
    serais-ce le exit (EXIT_SUCCESS); que j'ai honteusement enlevé du case 0, qui ferait office de break explicite ?

    Citation Envoyé par Obsidian Voir le message
    Et ce n'est pas tout, tu joues à la fois avec le TLS, avec les variables locales à l'intérieur des boucles qui deviennent indéfinies d'un tour à l'autre même dans un seul fil, avec l'héritage des descripteurs de fichiers au travers d'un fork et en plus, le pointeur « client » porte le même nom que la fonction dans laquelle il est défini et masque le nom de cette fonction alors que, par ailleurs, on y fait référence justement pour créer les nouveaux threads. Tout cela est extrêmement périlleux.
    TLS ? Thread Local Storage ? Bon, il faut vraiment que je consolide mes bases, si je souhaite une bonne compréhention globale des mécanismes en jeu.

    Concernant le pointeur client et sa fonction, ce code est proposé en l'état par l'auteur du livre... Devrais-je donc remettre en question le coding style de l'auteur ? J'en prends note pour l'avenir.

    La fonction client() telle que dans mon ouvrage de référence:
    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
     
    void *
    client (void *userdata)
    {
      client_t *client = userdata;
      char hello[] = "Bonjour, vous\n";
      int i;
     
      printf ("Nouveau thread\n");
     
    /* Différence avec le code précédent : il est encapsulé dans une
     * boucle pour utiliser le même thread pour plusieurs connexions.
     */
      for (i = 0; i < NB_CONNECTIONS_PAR_THREAD; i++)
        {
    /* Différence avec le code précédent : client->socket_id n'est
     * pas forcément initialisé : nous attendons qu'il le soit pour
     * communiquer avec le client.
     */
          while (client->socket_id == -1)
            sleep (1);
          write (client->socket_id, hello, strlen (hello));
          shutdown (client->socket_id, 2);
          close (client->socket_id);
          client->socket_id = -1;
        }
    /* Différence avec le code précédent : à chaque thread
     * est associée une structure. La présence de
     * client->exist == 1 indique son existence.
     */
      client->exist = 0;
      printf ("Fin du thread\n");
      return (NULL);
    }
    Citation Envoyé par Obsidian Voir le message
    En fait, le seul thread qui fonctionne est celui créé dans create_tcp_server(). La boucle principale en bas de la fonction main() est inopérante. Elle souffre de plusieurs tares mais je crois (il est tard et j'ai cessé d'explorer plus avant les méandres de ton code) que tu ne surveille pas la fin de ton processus fils : celui-ci engendre des threads puis se termine normalement en atteignant la fin de la fonction. Seulement, les threads font justement partie intégrante du processus en question, et disparaissent automatiquement si celui-ci prend fin.
    Bien... bien... bien... Après tout ces éléments de compréhension et ces pistes de raisonnement, je suis comblé. Je n'ai plus qu'a fournir les efforts nécessaires pour progresser, les outils me sont livrés. Merci.

    Citation Envoyé par Obsidian Voir le message
    Attention, certains pourraient te prendre au mot…
    La prochaine fois, je mettrais une balise "mode parabole stylistique rédactionelle ON"

    Citation Envoyé par Obsidian Voir le message
    C'est généralement mieux qu'une page blanche en effet mais, dans le cas présent, ça engendre plus de confusion qu'autre chose. Il faudrait au contraire partir toi-même du plus simple et complexifier progressivement ton propre programme, en utilisant éventuellement plusieurs copies de ton code source pour suivre ta progression ou, mieux, utiliser un logiciel de versionning tel que git ou mercurial.

    Bon courage.
    Je vais tenter les deux methodes en paralèlle. J'ai déja bricolé avec GIT et ça m'a plus. Je crois que je vais effectivement en profiter pour faire ça un peu plus "propre".
    Merci beaucoup pour toutes ces indications et le temps passé a la relecture de mon code, qui ne doit rien avoir de plaisant a lire, vu le mélange de portions reprises, de modifications hasardeuses, et le manque de commentaires.

    Quand a toi, forum, merci d'avoir permis a cet individu (caillou volcanique, je crois ?) de prendre connaissance de mon désaroi et d'y porter remède.

    Cordialement, bien a toi, Ô forum tout-puissant.

Discussions similaires

  1. [Débutant - XSLT] instructions serveurs possibles ?
    Par sempire dans le forum XSL/XSLT/XPATH
    Réponses: 1
    Dernier message: 06/10/2005, 11h06
  2. [débutant]Ajouter un Serveur à un Serveur Group
    Par AF_STjohn dans le forum MS SQL Server
    Réponses: 11
    Dernier message: 10/06/2005, 16h42
  3. [Débutant] Utilisation du debugger Eclipse avec Serveur Web
    Par tnodev dans le forum Eclipse Java
    Réponses: 1
    Dernier message: 09/06/2005, 17h45
  4. [Débutant] Serveur Corba
    Par antony dans le forum CORBA
    Réponses: 5
    Dernier message: 17/08/2004, 12h36
  5. [Débutant] Application client serveur
    Par dk dans le forum Plateformes (Java EE, Jakarta EE, Spring) et Serveurs
    Réponses: 7
    Dernier message: 30/06/2004, 11h38

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