C'est l'erreur 10038
J'ai appliqué ta sémantique, ça renvoie :
Unknown WSA error
C'est l'erreur 10038
J'ai appliqué ta sémantique, ça renvoie :
Unknown WSA error
Oui, il faut probablement développer ce code. Je ne traite que 4 cas... Je ne vais quand même pas tout faire ?Envoyé par sorry60
Je suis pas un as de l'anglais, mais si je comprend bien, mon fds_lecture n'est pas valideWSAENOTSOCK
Error Number: 10038
Socket operation on non-socket.
An operation was attempted on something that is not a socket.
Either the socket handle parameter did not reference a valid
socket, or for select, a member of an fd_set was not valid.
Pourtant sous linux il est ok, et avant le select, je le remet à neuf...étrange et ennuyeux
J'ai trouvé le bug ! Enfin au moins un bug
Quand il y a deconnexion, je fais ceci :
Mais cela doit etre mal fait...
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8 printf("Deconnexion de %s...\n",unClient->login); /* On le retire de l'ensemble de lecture */ FD_CLR(unClient->fd,&fds_lecture); /* On le retire de la liste */ retirer_client(&deb_liste,unClient->fd); /* On ferme la socket */ closesocket(unClient->fd);
Voici la reconsititution du fd_set, j'ai mis un printf pour savoir quelle socket est mise dedans :
Au debut :
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13 FD_ZERO(&fds_lecture); unClient = deb_liste; while ( unClient ) { if ( max < unClient->fd) { max = unClient->fd; } printf("Socket : %d\n",unClient->fd); FD_SET(unClient->fd,&fds_lecture); unClient = unClient->suivant; }
C'est la socket d'ecoute qui est mise dans le fd_set..jusqu'ici tout va bienSocket : 96
Ensuite, apres une connection :
124 etant la socket du nouveau client, donc ici tout va bien aussiSocket : 124
Socket : 96
Je deconnecte le client..
Et voila le bug..la socket est toujours là et surtout elle n'est plus valide !Socket : 830362444
Socket : 96
Donc je pense que c'est ma fonction "retirer_client" qui foire
Voici le code :
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 void retirer_client(client **p,SOCKET fd) { /* si la liste est vide */ if ( !p ) { /* rien à faire...*/ return; } else { /* si la liste ne contient qu'un seul élément */ if ( !((*p)->suivant) ) { /* on libère l'element */ free(*p); /* on fait pointer la liste sur NULL */ p = NULL; return; } else { client *prec = malloc(sizeof (client)); if ( prec ) { /* sinon on parcourt la liste jusqu'au bon client*/ while ( p && ((*p)->fd != fd) ) { /* on sauvegarde l'element precédent */ prec = *p; /* on passe au client suivant */ *p = (*p)->suivant; } /* on saute l'element bientot supprimé */ prec->suivant = (*p)->suivant; /* on supprime l'element */ free(*p); /* le client precedent devient le client actuel */ *p = prec; } else { perror("malloc()"); } } } }
hé hé, fichier complété...Envoyé par sorry60
sorry60 -> En fait, elle est très mauvaise... Je vais essayer de corriger...
Edit: Voilà une fonction corrigée:
(Je l'ai compilée en -ansi, mais je ne l'ai pas testée)
Emmanuel : Sous Windows, pourquoi ne pas utiliser FormatMessage() pour tes messages d'erreur ? (Oui, ça marche aussi avec Winsock).
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 /* Fonction pour retirer un client de la liste chaînée --------------------------------------------------- */ /* Retourne 0 si succès, -1 si erreur (convention lib standard) */ int retirer_client( client **pt_pt_client /*[in/out]Liste chaînée à modifier*/, SOCKET sock /*[in]Socket du client à retirer de la liste*/ ) { client *pt_client = NULL; /* Si la liste est inexistante, on quitte */ if( pt_pt_client==NULL ) return -1; pt_client = *pt_pt_client; /* si la liste est vide, on quitte aussi */ if ( pt_client==NULL ) return 0; /* si la liste ne contient qu'un seul élément */ if ( pt_client->suivant==NULL ) { /* on libère l'element */ free(pt_client); /* on fait pointer la liste sur NULL */ /* Médinoc : ERREUR: tu ne modifies pas le pointeur de l'utilisateur... p = NULL; */ *pt_pt_client = NULL; return 0; } /* Médinoc : ?? Pourquoi une allocation dans une fonction pour retirer? client *prec = malloc(sizeof (client)); */ /* sinon on parcourt la liste jusqu'au bon client. Au fur et à mesure, on actualise pt_pt_client pour savoir quel pointeur changer lors de la suppression */ while ( pt_client ) { /* Si l'élément contient le socket à retirer, on le retire et on quitte */ if(pt_client->fd == sock) { /* On fait pointer le client précédent (ou le début de liste) sur le client suivant (qui peut être NULL) */ *pt_pt_client = pt_client->suivant; /* On supprime le client de la mémoire */ free(pt_client); /* On quitte */ break; } /* On actualise le pointeur de pointeur */ pt_pt_client = &(pt_client->suivant); /* On passe au client suivant */ pt_client = pt_client->suivant; /* ou pt_client = *pt_pt_client; */ } return 0; }
PS: Ce serait bien si la balise code pouvait parser les comentaires...
Ben oui, je me le demande encore.... Bonne idée.Envoyé par Médinoc
Voila, apres m'etre exercé sur les listes chainées, j'ai pu corriger mon code.
Maintenant, il a l'air de bien fonctionner, je le post, si jamais ça interesse quelqu'un, ou si vous voulez faire des remarques.
serveur.h
fonctions_liste.c
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 #ifndef H_SERVEUR #define H_SERVEUR #if defined(__linux) # define SOCKET_ERROR -1 # define INVALID_SOCKET -1 # define closesocket(s) close(s) # define SOCKET int #endif #define LOGIN_SIZE 16 #define MDP_SIZE 16 #define PORT 4000 /* ******************************************************************************************* */ /* *********************************** Couche liste ****************************************** */ /* Structure représentant un client */ typedef struct client { char login[LOGIN_SIZE ]; char passwd[MDP_SIZE]; SOCKET fd; struct client* pSuivant; }client; /* Structure de liste */ typedef struct list { struct client* pTete; struct client* pQueue; }list; /* Fonction qui ajoute un client au debut la liste */ void ajouter_client (list *p, char log[LOGIN_SIZE], char mdp[MDP_SIZE ], SOCKET fd); /* Fonction qui retire un client de la liste */ void retirer_client(list *p,SOCKET fd); /* Fonction qui affige le login de tous les clients de la liste*/ void afficherListe(list *p); /* Fonction qui vide la mémoire occupée par le liste */ void clear_clients(list *); /* ******************************************************************************************* */ /* ******************************************************************************************* */ /* ******************************************************************************************* */ /* ********************************* Couche Serveur ****************************************** */ /* Fonction qui authentifie un utilisateur */ int authentification(char user[]); /* ******************************************************************************************* */ /* ******************************************************************************************* */ #endif
fonctions_serveur.c
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 #ifdef __cplusplus #error Be sure you are using a C compiler... #endif #include "serveur.h" #include <stdlib.h> #include <stdio.h> #include <string.h> /* ********************************************************************************************************************** */ void ajouter_client (list *p, char log[LOGIN_SIZE], char mdp[MDP_SIZE ], SOCKET fd) { client *nouv = malloc(sizeof(*nouv)); if(nouv) { memcpy(nouv->login,log,LOGIN_SIZE); memcpy(nouv->passwd,mdp,MDP_SIZE); nouv->fd = fd; /* Si la liste est vide */ if( !p->pTete ) { /* Le nouvel element est à la fois tete et queue */ p->pTete = nouv; p->pTete->pSuivant = NULL; p->pQueue = nouv; p->pQueue->pSuivant = NULL; } else { nouv->pSuivant = p->pTete; p->pTete = nouv; } } else { perror("malloc()"); exit (1); } } /* ********************************************************************************************************************** */ void retirer_client(list *p,SOCKET fd) { /* Si la liste ne contient qu'un element */ if ( (!p->pTete->pSuivant) && (p->pTete->fd == fd) ) { free(p->pTete); p->pTete = NULL; p->pQueue = NULL; return; } /* Si l'element a supprimer est en tete de liste */ if( p->pTete->fd == fd ) { client *tmp = p->pTete->pSuivant; free(p->pTete); p->pTete = tmp; return; } /* Sinon */ { client *prec = p->pTete; client *tmp = p->pTete; /* On se positionne sur l'element a supprimer */ while( (tmp) && (tmp->fd != fd) ) { prec = tmp; tmp = tmp->pSuivant; } if ( tmp ) { /* Si l'element est en queue de liste */ if ( p->pQueue == tmp ) { free(p->pQueue); p->pQueue = prec; p->pQueue->pSuivant = NULL; } else { prec->pSuivant = tmp->pSuivant; free(tmp); tmp = prec; } } else { printf("Cet element n'est pas dans la liste.\n"); } } } /* ********************************************************************************************************************** */ void afficherListe(list *p) { client *tmp = p->pTete; while(tmp) { printf("%s : %d\n",tmp->login,tmp->fd); tmp = tmp->pSuivant; } }
princ.c
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 #ifdef __cplusplus #error Be sure you are using a C compiler... #endif #include "serveur.h" #include <stdio.h> #include <string.h> /* ********************************************************************************************************************** */ /* Fonction qui authentifie le client qui tente de se connecter */ /* Revoie 0 si authentification reussie, 404 si l'utilisateur est inconnu, ou 1 en cas d'erreur */ int authentification(char user[]) { FILE* FP; int err = 0; /*Ouverture du fichier contenant les utilisateurs */ FP = fopen("./users.txt","r"); if ( FP != NULL ) { char buf[256]; char user_courant[128]; int eof = 0; do { if ( fgets(buf,sizeof buf,FP) == NULL ) { eof = 1; } else { sscanf(buf,"%s",user_courant); } } while ( (eof == 0) && (strcmp(user,user_courant) != 0)); if ( eof == 0 ) { printf("%s vient de se connecter...\n",user_courant); } else { printf("Utilisateur inconnu !\n"); err = 404; } } else { perror("fopen()"); err = 1; } return err; } /* Fin de la fonction d'authentification */ /* ********************************************************************************************************************** */
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 #ifdef __cplusplus #error Be sure you are using a C compiler... #endif #if defined(_WIN32) # include <windows.h> #elif defined(__linux) # include <sys/socket.h> # include <arpa/inet.h> #else #error not defined for this platform #endif #include <sys/types.h> #include <sys/time.h> #include <unistd.h> #include <stdio.h> #include "serveur.h" /* ************************************************************************************************************************* */ int main() { #if defined (_WIN32) WSADATA wsa_data; if ( WSAStartup(MAKEWORD(2,2),&wsa_data) != 0 ) { perror("WSAStartup()"); return 1; } #endif list lesClients; int err = 0; SOCKET max = 0; SOCKET s_ecoute; /*Ensemble de socket surveillées en lecture */ fd_set fds_lecture; FD_ZERO(&fds_lecture); lesClients.pTete = NULL; lesClients.pQueue = NULL; printf("La liste au debut :\n"); afficherListe(&lesClients); /* Création de la socket */ s_ecoute = socket(PF_INET,SOCK_STREAM,0); max = s_ecoute; if ( s_ecoute != INVALID_SOCKET ) { /* "bindage" de la socket */ struct sockaddr_in myaddr; struct sockaddr_in client_addr; myaddr.sin_family = AF_INET; myaddr.sin_port = htons(PORT); myaddr.sin_addr.s_addr = (INADDR_ANY); err = bind(s_ecoute,(struct sockaddr *) &myaddr, sizeof (myaddr)); if ( err != SOCKET_ERROR ) { /* Ecoute sur le port */ printf("Attente de connexion sur le port %d...\n",PORT); err = listen(s_ecoute,10); if ( err != SOCKET_ERROR ) { /* On met la socket d'ecoute dans l'ensemble de lecture */ FD_SET(s_ecoute,&fds_lecture); /* On ajoute la socket d'ecoute dans la liste */ ajouter_client(&lesClients,"","",s_ecoute); while(1) { printf("La liste au debut du while(1) :\n"); afficherListe(&lesClients); client *tmp = lesClients.pTete; /* on reconstitue le fds_lecture */ FD_ZERO(&fds_lecture); while (tmp) { printf("La liste au debut du while(tmp) :\n"); afficherListe(&lesClients); if ( max < tmp->fd) { max = tmp->fd; } FD_SET(tmp->fd,&fds_lecture); tmp = tmp->pSuivant; } if ( select(max+1,&fds_lecture,NULL,NULL,NULL) != -1 ) { /* On se repositionne en debut de liste et on la parcourt */ tmp = lesClients.pTete; while( tmp ) { /* Si il sait passer quelque chose dans l'emsemble de lecture avec ce client */ if ( FD_ISSET(tmp->fd,&fds_lecture)) { /* Si c'est la socket d'ecoute ==> nouvelle connexion ! */ if ( tmp->fd == s_ecoute ) { unsigned int size_client_addr = sizeof(client_addr); SOCKET newClient = accept(s_ecoute,(struct sockaddr *)&client_addr,&size_client_addr); int oct_rec; if ( newClient != INVALID_SOCKET ) { char login[LOGIN_SIZE]; printf("Connexion d'un nouveau client : %s socket : %d...\n",inet_ntoa(client_addr.sin_addr),newClient); /* On attend la réception de son login */ oct_rec = recv(newClient,login,sizeof(login),0); if ( oct_rec != - 1) { /* On procède à l'authentification du login */ int authent = authentification(login); if ( authent == 0 ) { /* Authentification reussie ! */ /* On met le nouveau client dans la liste */ ajouter_client(&lesClients,login,"",newClient); /* On le met aussi dans l'ensemble de lecture */ FD_SET(newClient,&fds_lecture); } else { /* Client inconnu ! */ char erreur404[] = "Desole vous n'etes pas dans la base de donnees...\n"; int oct_env; /* On envoie un message d'erreur au client inconnu */ oct_env = send(newClient,erreur404,sizeof(erreur404),0); if ( oct_env == -1 ) { perror("send() 404error"); } /* On ferme la socket */ closesocket(newClient); } } else { perror("revc() login"); } } else { perror("accept()"); } } else { /* Le client en cours a fait une action ! */ printf("Action de %s !\n",tmp->login); int oct_rec; char message[144]; /* 16 premiers octets pour le login du destinataire, le reste c'est le message */ oct_rec = recv(tmp->fd,message,sizeof(message),0); if ( oct_rec <= 0 ) { /* Erreur ou deconnexion client */ printf("Deconnexion de %s...\n",tmp->login); /* On ferme la socket */ closesocket(tmp->fd); /* On le retire de l'ensemble de lecture */ FD_CLR(tmp->fd,&fds_lecture); /* On le retire de la liste */ retirer_client(&lesClients,tmp->fd); } else { /* Le client a envoye un message ! */ printf("%s a envoyé %d octets !\n",tmp->login,oct_rec); } } } tmp = tmp->pSuivant; } /* Fin du parcour de la liste */ } else { perror("select()"); } } /* Fin de la boucle infinie */ } else { perror("listen()"); } } else { perror("bind()"); } } else { perror("socket()"); err = 1; } #if defined (_WIN32) WSACleanup(); #endif return err; } /* ********************************************************************************************************************** */
Tu devrais maintenant travailler à la généricité du code, c'est à dire à le rendre indépendant des données. (En gros, un void* suffit).Envoyé par sorry60
Mais une petite partie des traitements doit alors être confiée à l'appelant sous forme de callbacks (comme fait qsort()).
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6 struct node { void *p_data struct node *p_next; };
Le *void pointrait un coup sur un SOCKET, un coup sur un char[] c'est ça ?Envoyé par Emmanuel Delahaye
Je vais regarder dans le K&R si il y a un chapitre dessus
Par exemple. Evidemment, la durée de vie de l'objet pointé doit être >= à celle de la liste...Envoyé par sorry60
Pas la peine !Je vais regarder dans le K&R si il y a un chapitre dessus
Arf..ça va être dur sans cours et exempleEnvoyé par Emmanuel Delahaye
Un exemple standard : qsort()...Envoyé par sorry60
Sinon voir ça :
http://emmanuel-delahaye.developpez.com/complog.htm
et ça:
http://emmanuel-delahaye.developpez.com/tad.htm
Vous avez un bloqueur de publicités installé.
Le Club Developpez.com n'affiche que des publicités IT, discrètes et non intrusives.
Afin que nous puissions continuer à vous fournir gratuitement du contenu de qualité, merci de nous soutenir en désactivant votre bloqueur de publicités sur Developpez.com.
Partager