Gestion des event avec WSANETWORKEVENTS

Spiffou Messages postés 100 Date d'inscription jeudi 1 avril 2004 Statut Membre Dernière intervention 9 juin 2014 - 10 déc. 2004 à 19:11
Spiffou Messages postés 100 Date d'inscription jeudi 1 avril 2004 Statut Membre Dernière intervention 9 juin 2014 - 27 déc. 2004 à 16:20
bonjour à tous,
je voudrais votre avis sur la gestion des evenements sur les sockets.
je réalise une application (un jeu) qui doit communiquer sur un réseau local. j prévois donc d'utiliser 2 thread, un d'envoi et un de reception.

j'ai plusieurs pb:
- la gestion des evenement ne marche pas, en effet, je crée bien les evenements avec (WSACreateEvent) et je les assigne au socket (WSAEventSelect). par contre, le pb se situe j pense au niveau de l'attente d'evenement (WSAWaitForMultipleEvents) parce que le programme semble se bloquer à ce niveau.

- de plus, lors de la gestion du serveur, je ne vois pas comment gérer les evenements après acceptation de la connexion (accept) car il y a création d'un autre socket pour les communication.
(j'ai bien lu l'histoire dans laquelle on crée un tableau cf le livre conseillé par aardman qui est tres bien; mais j n'arrive pas à l'implémenter) j'ai donc choisi de créer un autre évenement sans faire tte l'histoire du tableau mais ca ne marche pas, le prog se bloque à nouveau sur WSAWaitForMultipleEvents.

- enfin rassurez moi, il m'a semble que d'utiliser la gestion de socket avec WSAWaitForMultipleEvents etait un bon choix, j'ai bon...??

Désolé pour la longueur du post, et pour ceux qui auront le courage de lire, merci beaucoup pour vos réponses.

//
// Voila le Code Client:
//

#include <stdio.h>
#include <winsock2.h>
#pragma comment(lib, "ws2_32.lib")

int main(int argc, char ** argv)
{
char buffer[250];
int N_port = 6566; // numero du port de communication
int QUEUE = 5; // taille de la file d'attente pour la connection sur le listen socket
SOCKET C_socket;
SOCKADDR_IN C_adresse; // adresse du client

WSAEVENT hEvent[1];
WSANETWORKEVENTS NetworkEvent;

printf("Chat Client\n");

// initialisation de la connection
C_socket = socket(AF_INET, SOCK_STREAM, 0);
if(C_socket == -1)
{
printf("ERREUR : echac de la création de socket\n");
}

C_adresse.sin_family = AF_INET;
C_adresse.sin_port = htons(N_port);
C_adresse.sin_addr.s_addr = inet_addr("127.0.0.1");

hEvent[0] = WSACreateEvent();
WSAEventSelect(C_socket, hEvent[0], FD_WRITE | FD_READ | FD_CONNECT | FD_CLOSE);

printf("Demande de connexion au serveur 127.0.0.1\n");
connect(C_socket, (sockaddr*)&C_adresse, sizeof(C_adresse));

// Boucle de gestion des evenements
while(true)
{
printf("Client Gestion Event\n");

memset(&NetworkEvent, 0, sizeof(NetworkEvent));
WSAWaitForMultipleEvents(1, hEvent, 0, WSA_INFINITE, 0);
WSAEnumNetworkEvents(C_socket, hEvent[0], &NetworkEvent);

if(NetworkEvent.lNetworkEvents & FD_CONNECT)
{
printf("Client Event Connect\n");
// erreur a la connexion
if(NetworkEvent.iErrorCode[FD_CONNECT_BIT])
{
printf ("ERREUR : Connexion error\n");
break;
}
// si connexion ok, envoi de la requete
}

if (NetworkEvent.lNetworkEvents & FD_WRITE)
{
printf("Client Event Write\n");
if (NetworkEvent.iErrorCode[FD_WRITE_BIT] != 0)
{
printf("FD_WRITE failed with error %d\n", NetworkEvent.iErrorCode[FD_WRITE_BIT]);
break;
}
// envoie de données
send(C_socket,
buffer,
sizeof(buffer),
0);
}

if(NetworkEvent.lNetworkEvents & FD_READ)
{
printf("Client Event Read\n");
// erreur a la connexion
if(NetworkEvent.iErrorCode[FD_READ_BIT])
{
printf ("ERREUR : Read error\n");
break;
}
// lecture de donnees sur le socket
recv(C_socket,buffer, sizeof(buffer), 0);
}

if(NetworkEvent.lNetworkEvents & FD_CLOSE)
{
printf("Client Event Close\n");
// verifie s'il ne reste plus rien a lire sur le socket
do
{
memset(&NetworkEvent, 0, sizeof(NetworkEvent));
WSAEnumNetworkEvents(C_socket, 0, &NetworkEvent);
if(NetworkEvent.lNetworkEvents & FD_READ)
{
if(NetworkEvent.lNetworkEvents & FD_READ)
{
// erreur a la connexion
if(NetworkEvent.iErrorCode[FD_READ_BIT])
{
printf ("ERREUR : Read error\n");
break;
}
// lecture de donnees sur le socket
recv(C_socket,buffer, sizeof(buffer), 0);
}
}
}
while(NetworkEvent.lNetworkEvents & FD_READ);

}
}
return 0;
}

//
// Voila le Code Serveur:
//

// serveur.cpp : Defines the entry point for the console application.
//
#include "string.h"
#include "stdio.h"
#include "conio.h"
#include "winsock2.h"
#pragma comment(lib, "ws2_32.lib")

void accept(void);

int main(int argc, char* argv[])
{
char buffer[250];
SOCKET L_socket; // socket pour ecoute sur le reseau (listen)
SOCKET S_socket; // socket Serveur pour emission/reception sur le reseau

SOCKADDR_IN S_adresse; // adresse du serveur

int N_port = 6667; // numero du port de communication
int QUEUE = 5; // taille de la file d'attente pour la connection sur le listen socket

int Ret;
WSADATA WSAData;

WSAEVENT hEvent[2];
WSANETWORKEVENTS NetworkEvent,NewEvent;

printf("Chat Serveur !\n");

// initialisation de la connexion

if ((Ret = WSAStartup(MAKEWORD(2,2), &WSAData)) != 0)
{
printf("ERREUR :WSAStartup a echoue",Ret);
return (-1);
}

L_socket = socket( AF_INET,SOCK_STREAM,IPPROTO_TCP);
S_adresse.sin_family = AF_INET;
S_adresse.sin_addr.s_addr = htonl(INADDR_ANY);
S_adresse.sin_port = htons(N_port);

bind( L_socket,(SOCKADDR *)&S_adresse,sizeof(S_adresse));

// creation d'un evenement reseau
hEvent[0] = WSACreateEvent();

WSAEventSelect(L_socket, hEvent[0], FD_ACCEPT | FD_CLOSE);

listen(L_socket, QUEUE);

while(TRUE)
{
memset(&NetworkEvent, 0, sizeof(NetworkEvent));
WSAWaitForMultipleEvents(1, hEvent, 0, WSA_INFINITE, 0);
WSAEnumNetworkEvents(L_socket, hEvent[0], &NetworkEvent);

// Check for FD_ACCEPT messages
if (NetworkEvent.lNetworkEvents & FD_ACCEPT)
{
printf("Serveur Event Accept\n");
if (NetworkEvent.iErrorCode[FD_ACCEPT_BIT] != 0)
{
printf("FD_ACCEPT failed with error %d\n",NetworkEvent.iErrorCode[FD_ACCEPT_BIT]);
break;
}

// Accept a new connection, and add it to the
// socket and event lists
accept();
}

if (NetworkEvent.lNetworkEvents & FD_CLOSE)
{
printf("Serveur Event Close\n");
if (NetworkEvent.iErrorCode[FD_CLOSE_BIT] != 0)
{
printf("FD_CLOSE failed with error %d\n",NetworkEvent.iErrorCode[FD_CLOSE_BIT]);
break;
}
closesocket(S_socket);
}
}

// fermeture du socket
//--------------------
closesocket(S_socket);
closesocket(L_socket);

// liberation des ressources allouées pour la communication
//---------------------------------------------------------
if (WSACleanup() == SOCKET_ERROR)
{
printf("ERREUR : WSACleanup a echoue, erreur : %d\n", WSAGetLastError());
}
return 0;
}

void accept(void)
{
printf("Serveur ConneX Accept\n");

S_socket = accept(
L_socket,
NULL,
NULL);

// send de test
send(S_socket,
"ca marche",
9,
0);

hEvent[1] = WSACreateEvent();

WSAEventSelect(
S_socket,
hEvent[1],
FD_READ | FD_WRITE | FD_CLOSE);

while(true)
{
memset(&NewEvent, 0, sizeof(NewEvent));

WSAWaitForMultipleEvents(1, hEvent, 0, WSA_INFINITE, 0);
WSAEnumNetworkEvents(S_socket, hEvent[1], &NewEvent);

// Process FD_READ notification
if (NewEvent.lNetworkEvents & FD_READ)
{
printf("Serveur Event Read\n");
if (NewEvent.iErrorCode[FD_READ_BIT] != 0)
{
printf("FD_READ failed with error %d\n", NewEvent.iErrorCode[FD_READ_BIT]);
break;
}

// Read data from the socket
recv(S_socket,buffer, sizeof(buffer), 0);
}

// Process FD_WRITE notification
if (NewEvent.lNetworkEvents & FD_WRITE)
{
printf("Serveur Event Write\n");
if (NewEvent.iErrorCode[FD_WRITE_BIT] != 0)
{
printf("FD_WRITE failed with error %d\n", NewEvent.iErrorCode[FD_WRITE_BIT]);
break;
}

send(S_socket,
buffer,
sizeof(buffer),
0);
}

if (NewEvent.lNetworkEvents & FD_CLOSE)
{
printf("Serveur Event Close\n");
if (NewEvent.iErrorCode[FD_CLOSE_BIT] != 0)
{
printf("FD_CLOSE failed with error %d\n",NewEvent.iErrorCode[FD_CLOSE_BIT]);
break;
}
closesocket(S_socket);
}
}

}

36 réponses

cs_aardman Messages postés 1905 Date d'inscription mercredi 22 janvier 2003 Statut Membre Dernière intervention 17 septembre 2012 3
10 déc. 2004 à 22:07
Salut,
Perso j'ai pas reussi a compiler le code, y'a trop d'erreur. La fonction accept() porte le nom d'une fonction winsock, et en plus elle utilise plein de variables auxquelles elle n'a pas acces.

Concernant le serveur:
Il faut tenir a jour une variable avec le nombre de clients connectés.
Lorsqu'un client se connecte, la fonction accept de winsock te retourne le socket qui servira a communiquer avec le client. Ensuite il faut crééer un nouvel Event, appeler WSAEventSelect() pour associer l'event au socket handle, puis ajouter l'Event dans le tableau d'event (celui que tu passes a WSAWaitForMultipleEvents).
Lors d'une déconnexion, il faut fermer l'event, fermer le socket, suprimer l'event du tableau et réarranger le tableau pour suprimer la case vide (l'event que tu as suprimé) car sinon WSAWaitForMultipleEvents provoquera une erreur.

Aussi, il faut tenir compte de la valeur de retour de WSAWaitForMultipleEvents, car c'est elle qui te permet de savoir quel event est passé dans l'état signalé.
0
Spiffou Messages postés 100 Date d'inscription jeudi 1 avril 2004 Statut Membre Dernière intervention 9 juin 2014 1
10 déc. 2004 à 23:45
ok merci d'avori jeté un ptit coup d'oeil...
en fait ca compil tres bien chez moi tout ca, mais mes variables C_Socket et C_adresse etc... sont declarées en globales du coup la fonction accept y a bien acces. j'ai voulu compresser le code pour mettre sur le site et j'ai aps fait gaffe...

par contre en effet accept est un fonction winsock du coup il y a surment un pb à ce niveau.

Sinon, à mon avis le client marche (je me suis bcp inspiré de ton code pour le dl d'un fichier en ligne ;) ), masi le serveur à un pb de gestion de la fonction WSAWaitForMultipleEvents ...

justement je me perds un peu dans l'utilisation de la var de retour etc, en fait je ne sais plus trop comment faire quoi.
je vais reflechir à tout ca demain et quand ce sera clair je referrai une reponse plus sensée. (pour l'instant je vais plutot reviser mon exam de demain matin... sniff)

en tt cas, les fait de gerer la fonction accept dasn une fonction séparée(qui en plus s'appelle accept...) me semble pas tres approprié en cas de fermeture du socket de communication quand la fonction va retourner, on sera tjs dans la boucle de gestion des events du socket d'ecoute... en plus, au niveau thread et asynchrone, en admettant qu'il y ai d'autres demandes de connexion, elle ne seront pas prises en compte puisqu'on ne gere plus les events sur le socket d'ecoute ... je me trompe pas là si?
--> c'est ma tt ca...
je pense que je vais utiliser une autre technique pour mon appli, par ctre j'aimerai bien faire marcher tt ca histoire de pas avoir commencé à apprendre pour rien... et surtt de pas tt gacher en arretant en route.

merci bcp encore d'avoir regardé
0
cs_aardman Messages postés 1905 Date d'inscription mercredi 22 janvier 2003 Statut Membre Dernière intervention 17 septembre 2012 3
10 déc. 2004 à 23:56
Salut,
En effet la fonction accept ne sert a rien et bloque le code.
WSAWaitForMultipleEvents peut gerer jusqu'a 64 events, donc tu n'a besoin que d'une boucle while (et donc 1 seul thread) pour gerer tout les evenements reseau de 64 sockets differents.
0
Spiffou Messages postés 100 Date d'inscription jeudi 1 avril 2004 Statut Membre Dernière intervention 9 juin 2014 1
12 déc. 2004 à 19:49
voila j'ai corrigé mon code et j'ai bien utilisé la sortie de WSAWaitForMultipleEvents pour la gestion des evenements et des differents sockets...
le pb c'est que le prog ne fait rien, il n'établit mm pas la connexion et là je ne comprend pas pourquoi...
(ca semble etre tjs bloqué sur WSAWaitForMultipleEvents )
je n'ai pas encore géré la sortie de l'appli (fermeture des sockets et mise à jour des tableaux d'evenements) mais je ne pense pas que ce soit ca qui l'empeche de se connecter.

d'autre par pour envoyer des données, comment fait on pour déclencher l'event qui est sensé entrainer l'envoi du buffer. puisque l'envoi de donnée est a l'initiative de l'appli, je ne vois pas bien comment l'effectuer.

revoila mon code qui cette fois doit compiler sans erreur avec un simple copier/coller...

merci beaucoup à tous...

// CODE SERVEUR
//---------------

#include "string.h"
#include "stdio.h"
#include "conio.h"
#include "winsock2.h"
#pragma comment(lib, "ws2_32.lib")

// Définition des variables Globables :
//-------------------------------------
#define NB_CONNEC_MAX 2

SOCKET L_socket; // socket pour ecoute sur le reseau (listen)
SOCKET S_socket; // socket Serveur pour emission/reception sur le reseau

SOCKADDR_IN S_adresse; // adresse du serveur

int N_port = 6566; // numero du port de communication
int QUEUE = 5; // taille de la file d'attente pour la connection sur le listen socket

WSAEVENT NewEvent,EventArray[3];
WSANETWORKEVENTS NetworkEvent;
SOCKET SocketArray [WSA_MAXIMUM_WAIT_EVENTS];
DWORD EventTotal = 0;
DWORD Index, i;

int main(int argc, char* argv[])
{

int k=0,j=0;
int Ret;

WSADATA WSAData;
char buffer[250];
char *Sbuffer;
Sbuffer = (char*)malloc(250);
Sbuffer = strdup ("envoi du serveur");

printf("CHAT SERVEUR\n");

// initialisation de Winsock version 2.2
//--------------------------------------
if ((Ret = WSAStartup(MAKEWORD(2,2), &WSAData)) != 0)
{
printf("ERREUR :WSAStartup a echoue",Ret);
return (-1);
}

// Création du socket d'écoute sur le reseau
// (demande de connexion client)
//-------------------------------------------
L_socket = socket( AF_INET, // famille d'adresse du protocol (ici IPv4)
SOCK_STREAM, // type de socket du protocol (ici TPC/IP)
IPPROTO_TCP); // protocol utilisé (ici TCP/IP)

// remplissage de la structure d'adressage winsock
// pour les communication reseau en IPv4
//------------------------------------------------
S_adresse.sin_family = AF_INET;
S_adresse.sin_addr.s_addr = htonl(INADDR_ANY);
S_adresse.sin_port = htons(N_port);

// Bind du Socket
// association de l'adresse avec le socket
//----------------------------------------
bind( L_socket, (SOCKADDR *)&S_adresse, sizeof(S_adresse));

// creation d'un evenement reseau
NewEvent = WSACreateEvent();

WSAEventSelect(L_socket, NewEvent, FD_ACCEPT | FD_CLOSE );

// mise à l'écoute de demande de connection client
// on utilise une file d'attente de 5 place
//------------------------------------------------
listen(L_socket, QUEUE);

SocketArray[EventTotal] = L_socket;
EventArray[EventTotal] = NewEvent;
EventTotal++;

printf("en attente de demande de connexion\n");

while(TRUE)
{

WSAWaitForMultipleEvents(1, EventArray, 0, WSA_INFINITE, 0);
WSAEnumNetworkEvents(L_socket, EventArray[0], &NetworkEvent);

// Wait for network events on all sockets
Index = WSAWaitForMultipleEvents(EventTotal,EventArray, FALSE, WSA_INFINITE, FALSE);
// permet de deduire de quel socket vient l'evenement
Index = Index - WSA_WAIT_EVENT_0;

// Iterate through all events to see if more than one is signaled
for(i=Index; i < EventTotal ;i++)
{
Index = WSAWaitForMultipleEvents(1, &EventArray[i], TRUE, 1000, FALSE);
if ((Index != WSA_WAIT_FAILED) || (Index != WSA_WAIT_TIMEOUT))
{
Index = i;

WSAEnumNetworkEvents(
SocketArray[Index],
EventArray[Index],
&NetworkEvent);

// Check for FD_ACCEPT messages
if (NetworkEvent.lNetworkEvents & FD_ACCEPT)
{
if (NetworkEvent.iErrorCode[FD_ACCEPT_BIT] != 0 && EventTotal >= NB_CONNEC_MAX)
{
printf("FD_ACCEPT failed with error %d\n",NetworkEvent.iErrorCode[FD_ACCEPT_BIT]);
break;
}

printf("reception et acceptation de la connexion\n");

// Accept a new connection, and add it to the
// socket and event lists
int taille_adr = sizeof (S_adresse);
S_socket = accept(L_socket, (SOCKADDR *)&S_adresse, &taille_adr);

// creation d'un evenement reseau
NewEvent = WSACreateEvent();

WSAEventSelect(S_socket, NewEvent, FD_CLOSE | FD_READ | FD_WRITE);

SocketArray[EventTotal] = S_socket;
EventArray[EventTotal] = NewEvent;
EventTotal++;

printf("connexion acceptee\n");
}

if(NetworkEvent.lNetworkEvents & FD_READ)
{
// erreur a la connexion
if(NetworkEvent.iErrorCode[FD_READ_BIT])
{
printf("FD_READ failed with error %d\n", NetworkEvent.iErrorCode[FD_READ_BIT]);
break;
}
// Read data from the socket
recv(S_socket,buffer, sizeof(buffer), 0);
j++;
printf(">>%d - %s\n",j,buffer);
}

if(NetworkEvent.lNetworkEvents & FD_WRITE)
{
// erreur a la connexion
if(NetworkEvent.iErrorCode[FD_WRITE_BIT])
{
printf("FD_WRITE failed with error %d\n", NetworkEvent.iErrorCode[FD_WRITE_BIT]);
break;
}
// envoie de données
send(S_socket, Sbuffer, sizeof(Sbuffer), 0);
k++;
printf ("<<%d - %s\n",k,Sbuffer);

}

if(NetworkEvent.lNetworkEvents & FD_CLOSE)
{
if (NetworkEvent.iErrorCode[FD_CLOSE_BIT] != 0)
{
printf("FD_CLOSE failed with error %d\n",NetworkEvent.iErrorCode[FD_CLOSE_BIT]);
break;
}
// verifie s'il ne reste plus rien a lire sur le socket
do {
memset(&NetworkEvent, 0, sizeof(NetworkEvent));
WSAEnumNetworkEvents(S_socket, 0, &NetworkEvent);
if(NetworkEvent.lNetworkEvents & FD_READ)
{
}
}while(NetworkEvent.lNetworkEvents & FD_READ);
}
}
}
}

// fermeture du socket
//--------------------
closesocket(S_socket);

// liberation des ressources allouées pour la communication
//---------------------------------------------------------
if (WSACleanup() == SOCKET_ERROR)
{
printf("ERREUR : WSACleanup a echoue, erreur : %d\n", WSAGetLastError());
}
return 0;
}


// CODE CLIENT
//-------------
#include <stdio.h>
#include <winsock2.h>
#pragma comment(lib, "ws2_32.lib")

int main(int argc, char ** argv)
{
char buffer[250];
char *Sbuffer;
Sbuffer = (char*)malloc(250);
int i=0,j=0;

SOCKET C_socket;
SOCKADDR_IN C_adresse; // adresse du client
int N_port = 6566; // numero du port de communication

WSAEVENT hEvent[1];
WSANETWORKEVENTS NetworkEvent;

printf ("CHAT CLIENT\n");

Sbuffer = strdup("envoi du client");

// initialisation de la connexion
C_socket = socket(AF_INET, SOCK_STREAM, 0);
if(C_socket == -1)
{
}

C_adresse.sin_family = AF_INET;
C_adresse.sin_port = htons(N_port);
C_adresse.sin_addr.s_addr = inet_addr("127.0.0.1");

hEvent[0] = WSACreateEvent();
WSAEventSelect(C_socket, hEvent[0], FD_WRITE | FD_READ | FD_CONNECT | FD_CLOSE);

connect(C_socket, (sockaddr*)&C_adresse, sizeof(C_adresse));
printf ("en attente de connexion\n");

// gestion des evenement
while(true)
{
memset(&NetworkEvent, 0, sizeof(NetworkEvent));

WSAWaitForMultipleEvents(1, hEvent, 0, WSA_INFINITE, 0);
WSAEnumNetworkEvents(C_socket, hEvent[0], &NetworkEvent);

// verification que la connexio s'est bien faite
if(NetworkEvent.lNetworkEvents & FD_CONNECT)
{
// erreur a la connexion
if(NetworkEvent.iErrorCode[FD_CONNECT_BIT])
{
printf("Erreur lors de la connexion.\n");
break;
}
// si connexion ok, envoi de la requette
printf ("connexion etablie\n");
}

if(NetworkEvent.lNetworkEvents & FD_READ)
{
// erreur a la connexion
if(NetworkEvent.iErrorCode[FD_READ_BIT])
{
printf("FD_READ failed with error %d\n", NetworkEvent.iErrorCode[FD_READ_BIT]);
break;
}
// Read data from the socket
recv(C_socket,buffer, sizeof(buffer), 0);
j++;
printf(">>%d - %s\n",j,buffer);
}

if(NetworkEvent.lNetworkEvents & FD_WRITE)
{
// erreur a la connexion
if(NetworkEvent.iErrorCode[FD_WRITE_BIT])
{
printf("FD_WRITE failed with error %d\n", NetworkEvent.iErrorCode[FD_WRITE_BIT]);
break;
}
// envoie de données
send(C_socket, Sbuffer, sizeof(Sbuffer), 0);
i++;
printf ("<<%d - %s\n",i,Sbuffer);

}

if(NetworkEvent.lNetworkEvents & FD_CLOSE)
{
if (NetworkEvent.iErrorCode[FD_CLOSE_BIT] != 0)
{
printf("FD_CLOSE failed with error %d\n",NetworkEvent.iErrorCode[FD_CLOSE_BIT]);
break;
}
// verifie s'il ne reste plus rien a lire sur le socket
do {
memset(&NetworkEvent, 0, sizeof(NetworkEvent));
WSAEnumNetworkEvents(C_socket, 0, &NetworkEvent);
if(NetworkEvent.lNetworkEvents & FD_READ)
{
}
}while(NetworkEvent.lNetworkEvents & FD_READ);
}
}
return 0;
}
0

Vous n’avez pas trouvé la réponse que vous recherchez ?

Posez votre question
cs_aardman Messages postés 1905 Date d'inscription mercredi 22 janvier 2003 Statut Membre Dernière intervention 17 septembre 2012 3
12 déc. 2004 à 20:06
Salut,
coté serveur j'ai pas vraiment compris ce que tu faisais:

Tu appeles WSAWaitForMultipleEvents, puis WSAEnumNetworkEvents, puis tu reappeles WSAWaitForMultipleEvents, ce qui donne:

Lorsque EventArray[0] passe a l'etat signalé, le 1er WSAWaitForMultipleEvents retourne, puis WSAEnumNetworkEvents se charge de remetre l'event a l'etat non signalé, et donc le 2eme WSAWaitForMultipleEvents bloque tout ...

voila un probleme, je suis pas allé plus loin.
0
Spiffou Messages postés 100 Date d'inscription jeudi 1 avril 2004 Statut Membre Dernière intervention 9 juin 2014 1
12 déc. 2004 à 20:34
en effet, un bete oubli dans le code... par ctre ca ne change rien au pb, le blocage semble tjs etre le mm...

je dois mal comprendre ton livre sur la prog reseau...

en tt cas je n'arrive pas à passer le premier WSAWaitForMultipleEvents, il se bloque dessus...
---> il n'y a donc pas d'evenement reseau?? pourtant je crois que mon initialisation de connexion est valide...
0
cs_aardman Messages postés 1905 Date d'inscription mercredi 22 janvier 2003 Statut Membre Dernière intervention 17 septembre 2012 3
12 déc. 2004 à 20:49
Salut,
Perso j'arrive a me connecter au server avec telnet donc le serveur accepte bien la 1ere connexion.

Par contre quand je tape quelque chose avec telnet ca m'affiche plein de caractere, c'est parceque tu fait directement un printf sur ce que tu recois sans rajouter le 0 final de la chaine.
0
cs_aardman Messages postés 1905 Date d'inscription mercredi 22 janvier 2003 Statut Membre Dernière intervention 17 septembre 2012 3
12 déc. 2004 à 20:56
Salut,
J'ai oublié une question que tu as posé plus haut, concernant l'envoie des donnés:
C'est simple, tu peux faire un send n'importe ou dans le code, et l'evenement FD_WRITE sera généré. Cet evenement te permet juste de verifier qu'il n'y a pas eu d'erreur lors de l'envoi.

Tu n'a pas a appeler send() lors du traitement de cet event, sauf dans certains cas précis, lorsque tu veux que les send se fassent automatiquement les uns a la suite des autres, par exemple dans le cas d'un transfers de fichiers.
0
Spiffou Messages postés 100 Date d'inscription jeudi 1 avril 2004 Statut Membre Dernière intervention 9 juin 2014 1
12 déc. 2004 à 22:37
oui merci pour le coup du zero, je corrige l'erreur...
j'en corrige aussi une autre:
recv(C_socket,buffer, strlen(buffer), 0);
en non pas
recv(C_socket,buffer, sizeof(buffer), 0);
car buffer est un pointeur donc 4 octets, rien à voir avec la taille reelle de la chaine... j'en profite pour proteger le printf en ajoutant le \0 de fin de chaine:
// Read data from the socket
nb_octets = recv(S_socket,buffer, strlen(buffer), 0);
if (nb_octets != -1)
{
buffer[nb_octets] = '\0';
j++;
printf(">>%d - %s\n",j,buffer);
}

par contre je suis pas sur de bien avoir compris pour le coup de send.
lorsque je fais send ailleurs dans mon prog, je declenche l'event FD_WRITE, et ca fait juste un controle d'erreur... (mais alors pkoi on recode le send dans la gestion de l'event FD_WRITE:
if(NetworkEvent.lNetworkEvents & FD_WRITE)
{
// erreur a la connexion
if(NetworkEvent.iErrorCode[FD_WRITE_BIT])
{
printf("FD_WRITE failed with error %d\n", NetworkEvent.iErrorCode[FD_WRITE_BIT]);
break;
}
// envoie de données
send(C_socket, Sbuffer, sizeof(Sbuffer), 0);
i++;
printf ("<<%d - %s\n",i,Sbuffer);
}

quand on fait ca ca ne fait pas un deuxieme send qui envoie buffer??
ou alors c'est l'histoire du transfert de fichier (ce qui n'est pas mon cas) donc il faudrait que je l'enlève c'est bien ca. c'est juste utiliser pour lancer l'envoi du paquet suivant quand les buffer est trop petit pour contenir la donnee à envoyer...

enfin, comme tu m'a dis qu'apparement le serveur acceptais les connexions, j suppose que mon client doit mal faire la demande, puisqu'il ne se passe rien je vais essayer de reverifier, mais il me semblait que tt etait correct coté client puisque c'est tres proche de ta source sur le transfert de ficheir en http et que j'avais testé mes parametres avant... ou alors j'ai encore mal implémenté un truc...

en tt cas merci pour tes conseils et bravo pour tes connaissances en prog socket...
^^
0
cs_aardman Messages postés 1905 Date d'inscription mercredi 22 janvier 2003 Statut Membre Dernière intervention 17 septembre 2012 3
12 déc. 2004 à 22:51
Salut,
j'en corrige aussi une autre:
recv(C_socket,buffer, strlen(buffer), 0);
en non pas
recv(C_socket,buffer, sizeof(buffer), 0);
Pas d'bol, c'etait bien sizeof(buffer) car buffer n'est pas un pointeur mais un tableau, et donc sizeof(buffer) sera remplacé par 250. Si tu met strlen(buffer), cela va retourner la taille de la chaine dans le buffer.. alors que tu n'est meme pas certain qu'il y en ai une valide (avec son \0 final).

Pour le send:
Tu travailles avec les socket non bloquants, c'est a dire que lorsque tu fais un send(...), le send retourne instantanément, meme si l'opération d'envoi n'est pas terminée. Une fois que l'opération est terminée, tu recois le FD_WRITE pour t'en avertir, et donc tu peux verifier si tu as eu une erreur.

Si tu met un send dans le FD_WRITE, en théorie ca devrait envoyer les données de buffer en boucle (faudrait tester ca)
0
Spiffou Messages postés 100 Date d'inscription jeudi 1 avril 2004 Statut Membre Dernière intervention 9 juin 2014 1
13 déc. 2004 à 01:03
ah oui merde... je me suis encore trompu... (l'habitude du malloc on va dire... :D )

j continue de chercher pour le client
0
Spiffou Messages postés 100 Date d'inscription jeudi 1 avril 2004 Statut Membre Dernière intervention 9 juin 2014 1
13 déc. 2004 à 01:06
ha la grosse blague...
il manquerai pas qqch dans le client? humm genre par exple :

// initialisation de Winsock version 2.2
//--------------------------------------
if ((Ret = WSAStartup(MAKEWORD(2,2), &WSAData)) != 0)
{
printf("ERREUR :WSAStartup a echoue",Ret);
return (-1);
}

c con hein comme erreur... bon ben now j'espère que ca va aller mieux...
0
Spiffou Messages postés 100 Date d'inscription jeudi 1 avril 2004 Statut Membre Dernière intervention 9 juin 2014 1
13 déc. 2004 à 01:17
ca y est ca marche, lorque l'on ajoute cette petite initialisation de winsock, d'un coup c'est magique...

par contre y'a une chose que je ne comprend pas bien:
lorsque la connexion se crée, FD_ACCEPT, je visualise coté serveur un envoie de donnée (en l'occurrence le buffer) et coté client un envoie de donne (le buffer aussi)

le client et le serveur recoivent bien les donnée (normal puisque now la gestion d'event marche) mais je ne vois pas pkoi ces envois de buffer ont été déclenchés alors que le FD_SEND n'aurait pas du etre activé...

en effet, il y a une génération de l'event FD_ACCEPT pour le serveur et FD_CONNECT pour le client mais dans la boucle de test de la nature de l"event, le code concernant FD_WRITE est aussi executé alors qu'il n'y a pas eu de demande d'envoi (les envois sont faits pq il y a un send dons mon code de gestion de l'event FD_WRITE (c'est mal et je les enleve) )
pourtant je ne pense pas que FD_ACCEPT et FD_WRITE soit indissociables... qqun a t il une explication à ca??

merci à tous et en particulier à aardman pour son aide...
0
cs_aardman Messages postés 1905 Date d'inscription mercredi 22 janvier 2003 Statut Membre Dernière intervention 17 septembre 2012 3
13 déc. 2004 à 01:34
Salut,
Pourrais tu reposter les codes du client et du serveur stp.
0
Spiffou Messages postés 100 Date d'inscription jeudi 1 avril 2004 Statut Membre Dernière intervention 9 juin 2014 1
13 déc. 2004 à 14:40
voila le code source qui maintenant marche...

sinon pour les envois de données à la connection dont j parle plus haut qqun (aardman en autre puisqu'apparement c le pro des socket ici...) sait-il à quoi c'est du??

///////////////////////
// CODE SERVEUR
///////////////////////
#include "string.h"
#include "stdio.h"
#include "conio.h"
#include "winsock2.h"
#pragma comment(lib, "ws2_32.lib")

// Définition des variables Globables :
//-------------------------------------
#define NB_CONNEC_MAX 2

SOCKET L_socket; // socket pour ecoute sur le reseau (listen)
SOCKET S_socket; // socket Serveur pour emission/reception sur le reseau

SOCKADDR_IN S_adresse; // adresse du serveur

int N_port = 6566; // numero du port de communication
int QUEUE = 5; // taille de la file d'attente pour la connection sur le listen socket

WSAEVENT NewEvent,EventArray[3];
WSANETWORKEVENTS NetworkEvent;
SOCKET SocketArray [WSA_MAXIMUM_WAIT_EVENTS];
DWORD EventTotal = 0;
DWORD Index, i;

int main(int argc, char* argv[])
{

int k=0,j=0;
int nb_octets;
int Ret;

WSADATA WSAData;
char buffer[250];
char *Sbuffer;
Sbuffer = (char*)malloc(250);
Sbuffer = strdup ("envoi du serveur");

printf("CHAT SERVEUR\n");

// initialisation de Winsock version 2.2
//--------------------------------------
if ((Ret = WSAStartup(MAKEWORD(2,2), &WSAData)) != 0)
{
printf("ERREUR :WSAStartup a echoue",Ret);
return (-1);
}

// Création du socket d'écoute sur le reseau
// (demande de connexion client)
//-------------------------------------------
L_socket = socket( AF_INET, // famille d'adresse du protocol (ici IPv4)
SOCK_STREAM, // type de socket du protocol (ici TPC/IP)
IPPROTO_TCP); // protocol utilisé (ici TCP/IP)

// remplissage de la structure d'adressage winsock
// pour les communication reseau en IPv4
//------------------------------------------------
S_adresse.sin_family = AF_INET;
S_adresse.sin_addr.s_addr = htonl(INADDR_ANY);
S_adresse.sin_port = htons(N_port);

// Bind du Socket
// association de l'adresse avec le socket
//----------------------------------------
bind( L_socket, (SOCKADDR *)&S_adresse, sizeof(S_adresse));

// creation d'un evenement reseau
NewEvent = WSACreateEvent();

WSAEventSelect(L_socket, NewEvent, FD_ACCEPT | FD_CLOSE );

// mise à l'écoute de demande de connection client
// on utilise une file d'attente de 5 place
//------------------------------------------------
listen(L_socket, QUEUE);

SocketArray[EventTotal] = L_socket;
EventArray[EventTotal] = NewEvent;
EventTotal++;

printf("en attente de demande de connexion\n");

while(TRUE)
{
// Wait for network events on all sockets
Index = WSAWaitForMultipleEvents(EventTotal,EventArray, FALSE, WSA_INFINITE, FALSE);
// permet de deduire de quel socket vient l'evenement
Index = Index - WSA_WAIT_EVENT_0;

// Iterate through all events to see if more than one is signaled
for(i=Index; i < EventTotal ;i++)
{
Index = WSAWaitForMultipleEvents(1, &EventArray[i], TRUE, 1000, FALSE);
if ((Index != WSA_WAIT_FAILED) || (Index != WSA_WAIT_TIMEOUT))
{
Index = i;

WSAEnumNetworkEvents(
SocketArray[Index],
EventArray[Index],
&NetworkEvent);

// Check for FD_ACCEPT messages
if (NetworkEvent.lNetworkEvents & FD_ACCEPT)
{
if (NetworkEvent.iErrorCode[FD_ACCEPT_BIT] != 0 && EventTotal >= NB_CONNEC_MAX)
{
printf("FD_ACCEPT failed with error %d\n",NetworkEvent.iErrorCode[FD_ACCEPT_BIT]);
break;
}

printf("reception et acceptation de la connexion\n");

// Accept a new connection, and add it to the
// socket and event lists
int taille_adr = sizeof (S_adresse);
S_socket = accept(L_socket, (SOCKADDR *)&S_adresse, &taille_adr);

// creation d'un evenement reseau
NewEvent = WSACreateEvent();

WSAEventSelect(S_socket, NewEvent, FD_CLOSE | FD_READ | FD_WRITE);

SocketArray[EventTotal] = S_socket;
EventArray[EventTotal] = NewEvent;
EventTotal++;

printf("connexion acceptee\n");
}

if(NetworkEvent.lNetworkEvents & FD_READ)
{
// erreur a la connexion
if(NetworkEvent.iErrorCode[FD_READ_BIT])
{
printf("FD_READ failed with error %d\n", NetworkEvent.iErrorCode[FD_READ_BIT]);
break;
}
// Read data from the socket
nb_octets = recv(S_socket,buffer, sizeof(buffer), 0);
if (nb_octets != -1)
{
buffer[nb_octets] = '\0';
j++;
printf(">>%d - %s\n",j,buffer);
}
}

if(NetworkEvent.lNetworkEvents & FD_WRITE)
{
// erreur a la connexion
if(NetworkEvent.iErrorCode[FD_WRITE_BIT])
{
printf("FD_WRITE failed with error %d\n", NetworkEvent.iErrorCode[FD_WRITE_BIT]);
break;
}
// envoie de données
send(S_socket, Sbuffer, strlen(Sbuffer)+1, 0);
k++;
printf ("<<%d - %s\n",k,Sbuffer);

}

if(NetworkEvent.lNetworkEvents & FD_CLOSE)
{
if (NetworkEvent.iErrorCode[FD_CLOSE_BIT] != 0)
{
printf("FD_CLOSE failed with error %d\n",NetworkEvent.iErrorCode[FD_CLOSE_BIT]);
break;
}
// verifie s'il ne reste plus rien a lire sur le socket
do {
memset(&NetworkEvent, 0, sizeof(NetworkEvent));
WSAEnumNetworkEvents(S_socket, 0, &NetworkEvent);
if(NetworkEvent.lNetworkEvents & FD_READ)
{
}
}while(NetworkEvent.lNetworkEvents & FD_READ);
}
}
}
}

// fermeture du socket
//--------------------
closesocket(S_socket);

// liberation des ressources allouées pour la communication
//---------------------------------------------------------
if (WSACleanup() == SOCKET_ERROR)
{
printf("ERREUR : WSACleanup a echoue, erreur : %d\n", WSAGetLastError());
}
return 0;
}

////////////////////
// CODE CLIENT
////////////////////
#include <stdio.h>
#include <winsock2.h>
#pragma comment(lib, "ws2_32.lib")

int main(int argc, char ** argv)
{
char buffer[250];
char *Sbuffer;
Sbuffer = (char*)malloc(250);
int i=0,j=0;
int Ret;
WSADATA WSAData;
SOCKET C_socket;
SOCKADDR_IN C_adresse; // adresse du client
int N_port = 6566; // numero du port de communication

WSAEVENT hEvent[1];
WSANETWORKEVENTS NetworkEvent;

printf ("CHAT CLIENT\n");

Sbuffer = strdup("envoi du client");

// initialisation de Winsock version 2.2
//--------------------------------------
if ((Ret = WSAStartup(MAKEWORD(2,2), &WSAData)) != 0)
{
printf("ERREUR :WSAStartup a echoue",Ret);
return (-1);
}

// initialisation de la connexion
C_socket = socket(AF_INET, SOCK_STREAM, 0);
if(C_socket == -1)
{
}

C_adresse.sin_family = AF_INET;
C_adresse.sin_port = htons(N_port);
C_adresse.sin_addr.s_addr = inet_addr("127.0.0.1");

hEvent[0] = WSACreateEvent();
WSAEventSelect(C_socket, hEvent[0], FD_WRITE | FD_READ | FD_CONNECT | FD_CLOSE);

connect(C_socket, (sockaddr*)&C_adresse, sizeof(C_adresse));
printf ("en attente de connexion\n");

// gestion des evenement
while(true)
{
memset(&NetworkEvent, 0, sizeof(NetworkEvent));

if (WSAWaitForMultipleEvents(1, hEvent, 0, WSA_INFINITE, 0) == WSA_WAIT_FAILED)
{
printf("ERREUR : WSAWaitForMultipleEvents");
break;
}
if (WSAEnumNetworkEvents(C_socket, hEvent[0], &NetworkEvent) == SOCKET_ERROR )
{
printf("ERREUR : WSAEnumNetworkEvents");
break;
}

// verification que la connexio s'est bien faite
if(NetworkEvent.lNetworkEvents & FD_CONNECT)
{
// erreur a la connexion
if(NetworkEvent.iErrorCode[FD_CONNECT_BIT])
{
printf("Erreur lors de la connexion.\n");
break;
}
// si connexion ok, envoi de la requette
printf ("connexion etablie\n");
}

if(NetworkEvent.lNetworkEvents & FD_READ)
{
// erreur a la connexion
if(NetworkEvent.iErrorCode[FD_READ_BIT])
{
printf("FD_READ failed with error %d\n", NetworkEvent.iErrorCode[FD_READ_BIT]);
break;
}
// Read data from the socket
recv(C_socket,buffer, sizeof(buffer), 0);
j++;
printf(">>%d - %s\n",j,buffer);
}

if(NetworkEvent.lNetworkEvents & FD_WRITE)
{
// erreur a la connexion
if(NetworkEvent.iErrorCode[FD_WRITE_BIT])
{
printf("FD_WRITE failed with error %d\n", NetworkEvent.iErrorCode[FD_WRITE_BIT]);
break;
}
// envoie de données
send(C_socket, Sbuffer, strlen(Sbuffer)+1, 0);
i++;
printf ("<<%d - %s\n",i,Sbuffer);

}

if(NetworkEvent.lNetworkEvents & FD_CLOSE)
{
if (NetworkEvent.iErrorCode[FD_CLOSE_BIT] != 0)
{
printf("FD_CLOSE failed with error %d\n",NetworkEvent.iErrorCode[FD_CLOSE_BIT]);
break;
}
// verifie s'il ne reste plus rien a lire sur le socket
do {
memset(&NetworkEvent, 0, sizeof(NetworkEvent));
WSAEnumNetworkEvents(C_socket, 0, &NetworkEvent);
if(NetworkEvent.lNetworkEvents & FD_READ)
{
}
}while(NetworkEvent.lNetworkEvents & FD_READ);
}
}
return 0;
}
0
cs_aardman Messages postés 1905 Date d'inscription mercredi 22 janvier 2003 Statut Membre Dernière intervention 17 septembre 2012 3
13 déc. 2004 à 16:25
Salut,
En fait c'est normal, je le savais pas, d'apres MSDN:
The FD_WRITE network event is handled slightly differently. An FD_WRITE network event is recorded when a socket is first connected with connect/WSAConnect or accepted with accept/WSAAccept.

Donc en fait les evenements FD_CONNECT et FD_ACCEPT generent tout les deux levenement FD_WRITE, et comme lors du traitement de cet évent tu appele send, les 2 programmes s'echangent une phrase lors de la connexion.
0
Spiffou Messages postés 100 Date d'inscription jeudi 1 avril 2004 Statut Membre Dernière intervention 9 juin 2014 1
13 déc. 2004 à 16:42
logique en effet.

bon ben reste plus qu'a optimiser le code. et à faire un zouli prog avec des threads etc...

merci pour ton aide aardman, j pense que ke j posterai une source quand tt ca sera opé d'ici là si il y a des conseils pour optimisation ou autre, ce sera avec plaisir...
0
cs_aardman Messages postés 1905 Date d'inscription mercredi 22 janvier 2003 Statut Membre Dernière intervention 17 septembre 2012 3
13 déc. 2004 à 18:36
Salut,
Coté client, il n'y a qu'a gerer un client (1 socket) donc j'en parlerais pas.

Mais coté serveur, il reste encore beaucoup de travail: il faut gerer les connexions, les déconnexions (fermer le sockets, fermer les events, ranger le tableau d'event & de socket, etc..).

Il faut etre sur de bien gerer tout les evenements aussi, tel que ton code est présenté, si 2 evenements (ou 3, ou +) passent a l'état signalés en meme temp, un seul sera traité !
La partie de code qui suit le commentaire "Iterate through all events to see if more than one is signaled" ne sert a rien, car tu ne geres les events FD_XXX qu'une seule fois apres.

Enfin, tu parles de threads.. pour gerer moins de 64 events, donc moins de 63 clients, il ne t'en faudra qu'un.. le probleme des threads se posera si tu souhaite de ton serveur puisse gerer plus de 63 clients.
0
Spiffou Messages postés 100 Date d'inscription jeudi 1 avril 2004 Statut Membre Dernière intervention 9 juin 2014 1
13 déc. 2004 à 20:33
// Iterate through all events to see if more than one is signaled
for(i=Index; i < EventTotal ;i++)
{
Index = WSAWaitForMultipleEvents(1, &EventArray[i], TRUE, 1000, FALSE);
...
...

il me semblait qu'avec ca on pouvait verifier tt les evenement du tableau, donc le pb c'est si il y a plusieur "event(evenement reseau) sur le mm event(element du tableau)" c'est bien ca??

par ctre je ne vois pas trop comment faire, mais j'ai pas fini de lire le chapitre sur les I/O du livre network programming for windows...

sinon au niveau des threads, vu que dans mon appli finale le reseau n'est qu'un module, je voudrais bien ne pas resté bloqué sur le WSAWaitForMultipleEvents... d'où le thread (en effet, il y en a qu'un).

enfin pour la fermeture du prog, j'ai bien vu qu'il fallait fermé l socket d'ailleurs il me semble que je le fait non? ah oui mais j'oublie de fermer le socket d'ecoute du reseau, sinon pour le rangement du tableau d'event j'ai vu dans le livre que tu conseilles qu'il fallait utiliser :
CompressArrays(EventArray, SocketArray, &EventTotal);
mais chez moi cette fonction ne marche pas... j'ai encore du mal faire un truc.
0
cs_aardman Messages postés 1905 Date d'inscription mercredi 22 janvier 2003 Statut Membre Dernière intervention 17 septembre 2012 3
13 déc. 2004 à 23:42
Salut,
Avec cette boucle tu trouves l'index des events qui sont signalés, mais tu n'en traite qu'un (le dernier).

Pour chaque evenement signalé, il faut appeler WSAEnumNetworkEvent(), et traiter tout les cas possibles pour cet evenement reseau (FD_READ, etc...).

Il faut donc faire une fonction qui est chargée de traiter un evenement reseau quelconque, et appeler cette fonction pour chaque event signalé que tu trouves dans ton tableau.

Cette fonction appelerait WSAEnumNetworkEvent() pour l'event & le socket concerné, et traiterais ensuite tout les cas possibles: FD_READ, FD_WRITE, etc.

Quant a la fonction CompressArrays:
Lorsque un client se deconnecte, il faut fermer son event, et du coup le tableau d'event contient un nombre qui n'est plus un event valide: la fonction WSAWaitForMultipleEvents plantera a coup sur.
C'est pour ca que la fonction CompressArrays existe, elle permet de virer les anciens events qui ne sont plus valides.
Cette fonction compresse aussi le tableau de socket, pour que l'on puisse garder la correspondance socket <-> event a partir d'un index dans les 2 tableaux. (par exemple, l'event EventArray[12] correspond au socket SocketArray[12]).
0
Rejoignez-nous