Spiffou
Messages postés100Date d'inscriptionjeudi 1 avril 2004StatutMembreDernière intervention 9 juin 2014
-
10 déc. 2004 à 19:11
Spiffou
Messages postés100Date d'inscriptionjeudi 1 avril 2004StatutMembreDerniè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.
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
// 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");
}
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
// 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;
}
Spiffou
Messages postés100Date d'inscriptionjeudi 1 avril 2004StatutMembreDernière intervention 9 juin 20141 14 déc. 2004 à 10:56
je dois pas avoir bie, compris ce que tu voulais dire, j'ai été voir sur les msdn, mais mon code reprend exactement leur exemple (il doit etre trop simpliste).
perso j'ai compris que plusieurs event pouvaient arriver en mm tps sur le mm socket, on a donc WSAWaitForMultipleEvents qui retourne (mais en fait plusieurs event sont actif) et il faut donc tous les tester.
donc mon cas j'utilise 4 event different: FD_READ FD_WRITE FD_ACCEPT FD_CLOSE, il faut donc que j fasse 4 fois WSAEnumNetworkEvent... c'est ca?
on aurait:
Index = WSAWaitForMultipleEvents(EventTotal,EventArray, FALSE, WSA_INFINITE, FALSE);
Index = Index - WSA_WAIT_EVENT_0;
for(i=Index; i < EventTotal ;i++)
{
Index = WSAWaitForMultipleEvents(1, &EventArray[i], TRUE, 1000, FALSE);
if ((Index != WSA_WAIT_FAILED) || (Index != WSA_WAIT_TIMEOUT))
{
for (j=0;j<4;j++)
{
Index = i;
WSAEnumNetworkEvents(
SocketArray[Index],
EventArray[Index],
&NetworkEvent);
//
// traitement de chaque type d'event
//
}
}
}
Spiffou
Messages postés100Date d'inscriptionjeudi 1 avril 2004StatutMembreDernière intervention 9 juin 20141 14 déc. 2004 à 11:02
au fait pour la fonciton CompressArrays il faut charger qqch de particulier ou pas?
pq j'ai essayé de l'utiliser, mais :
error C2065: 'CompressArrays' : undeclared identifier
et sur les msdn la recherche à error C2065: 'CompressArrays' n'a rien donné...
cs_aardman
Messages postés1905Date d'inscriptionmercredi 22 janvier 2003StatutMembreDernière intervention17 septembre 20123 14 déc. 2004 à 12:33
Salut,
undeclared identifier ? CompressArrays n'est pas une api, mais une fonction que tu dois coder toi meme.
"donc mon cas j'utilise 4 event different: FD_READ FD_WRITE FD_ACCEPT FD_CLOSE, il faut donc que j fasse 4 fois WSAEnumNetworkEvent... c'est ca?"
>> pas du tout, tu as autant d'event que de socket.
Imagine tu as 3 clients qui envoient en meme temp 3 messages sur le serveur. Tu va avoir 3 events qui vont passer a l'état signalés, il faut appeler WSAEnumNetworkEvent pour chaque event, et il faut gerer l'evenement FD_READ pour chaqu'un des 3 clients...
En fait, il faut gerer les evenement FD_CONNECT FD_READ FD_WRITE FD_ACCEPT FD_CLOSE dans une fonction, et appeler cette fonction 1 fois pour chaque event signalé.
Dans l'exemple de serveur du bouquin, c'est la fonction HandleIo qui fait cela (a etudier). D'ailleur dans cet exemple, tu dois aussi trouver la fonction CompressArrays.
// Enumerate the events
rc = WSAEnumNetworkEvents(
sock->s,
sock->event[index],
&nevents);
// traitement de chaque exeption
}
}
Le pb c'est que je vois mal la difference avec ce que j fais:
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;
en gros a part la fonction waitforsingleobject (qui d'ailleurs n'est pas une fction winsock, je l'ai pas vu sur msdn, et qui en plus n'est pas codee dans l'exple), ca me semble asser proche, je ne vois pas bien pkoi mon code ne teste pas les events de chaque socket puisque:
for (i=Index; i < EventTotal ;i++)
{
Index = WSAWaitForMultipleEvents(1, &EventArray[i], TRUE, 1000, FALSE);
ca devrait tester ts les events que l'on a cree et comme ils sont tous assignés à un socket ca me donne l'impression que l'on teste chaque type d'event pour chaque event (et donc socket) créé avec l'interieur de la boucle for:
Index = WSAWaitForMultipleEvents(1, &EventArray[i], TRUE, 1000, FALSE);
if ((Index != WSA_WAIT_FAILED) || (Index != WSA_WAIT_TIMEOUT))
{
Index = i;
cs_aardman
Messages postés1905Date d'inscriptionmercredi 22 janvier 2003StatutMembreDernière intervention17 septembre 20123 15 déc. 2004 à 00:19
Salut,
//
// Function: ChildThread
//
// Description:
// This is the child thread that handles socket connections. Each thread
// can only wait on a maximum of 63 sockets. The main thread will assign
// each client connection to one of the child threads. If there is no
// thread to handle the socket, a new thread is created to handle the
// connection.
//
DWORD WINAPI ChildThread(LPVOID lpParam)
{
THREAD_OBJ *thread=NULL;
SOCKET_OBJ *sptr=NULL,
*sockobj=NULL;
int index,
rc,
i;
thread = (THREAD_OBJ *)lpParam;
while (1)
{
rc = WaitForMultipleObjects(
thread->SocketCount + 1,
thread->Handles,
FALSE,
INFINITE
); if (rc WAIT_FAILED || rc WAIT_TIMEOUT)
{
fprintf(stderr, "ChildThread: WaitForMultipleObjects failed: %d\n", GetLastError());
break;
}
else
{
// Multiple events may be signaled at one time so check each
// event to see if its signaled
//
for(i=0; i < thread->SocketCount + 1 ;i++)
{
rc = WaitForSingleObject(thread->Handles[i], 0);
if (rc == WAIT_FAILED)
{
fprintf(stderr, "ChildThread: WaitForSingleObject failed: %d\n", GetLastError());
ExitThread(-1);
}
else if (rc == WAIT_TIMEOUT)
{
// This event isn't signaled, continue to the next one
continue;
}
index = i;
if (index == 0)
{
// If index 0 is signaled then rebuild the array of event
// handles to wait on
WSAResetEvent(thread->Handles[index]);
RenumberThreadArray(thread);
i = 1;
}
else
{
// Otherwise, its an event associated with a socket that
// was signaled. Handle the IO on that socket.
//
sockobj = FindSocketObj(thread, index-1);
if (sockobj != NULL)
{
if (HandleIo(thread, sockobj) == SOCKET_ERROR)
{
RenumberThreadArray(thread);
}
}
else
{
printf("Unable to find socket object!\n");
}
}
}
}
}
ExitThread(0);
return 0;
}
Bon, alors au debut de la boucle while, on appele WaitForMultipleObjects.
On traite la valeur de retour (WAIT_FAILED ou WAIT_TIMEOUT si erreur).
S'il n'y a pas d'erreur, on boucle dans le tableau d'event.
POUR CHAQUE EVENT:
on appele WaitForSingleObject (qui est une api, cherche mieux sur msdn).
Puis on teste la valeur de retour de WaitForSingleObject (WAIT_TIMEOUT ou WAIT_FAILED si erreur).
Si pas d'erreur, si pas de timeout, on a bien un event signalé: et on appele HandleIo.
HandleIo: appele WSAEnumNetworkEvent pour l'event concerné, et traite tout les cas possibles: FD_READ, FD_WRITE, etc...
Spiffou
Messages postés100Date d'inscriptionjeudi 1 avril 2004StatutMembreDernière intervention 9 juin 20141 15 déc. 2004 à 14:00
euh oui mais donc alors là j'ai une révélation...
dans l'exemple, le truc bizarre c'est qu'il n'utilise que les fonctions WaitForMultipleObjects et WaitForSingleObject qui sont des foncitons de synchronisation de thread (c pour ca que j l'ai pas trouver dans la lib winsock2) alors que moi j'utilise les fonctions WSAWaitForMultipleObjects... idem pour les events avec WSACreateEvent vs CreateEvent.
en fait le pb c'est que il cree des events de threads et les gere avec les fctions winsocks c possible ca??
pq en fait j pensais que les WSAEvent n'etait pas compatible avec les event de gestion des threads... mais apparement si.
du coup il faut connecter WaitForSingleObject(thread->Handles[i], 0); avec tt les events crees pour chaque socket (le tab EventArray[])
en fait, on verifie avec wsawaitformultipleobject qu'il y a eu un event, et on cherche quel l'event declenché en parcourant le tab d'event et en testant avec waitforsingleobject (comme on parcourt tt le tableau, si il y en a eu plusieurs d'un coup, ils seront tous detectés)
j'ai bien compris là ou aps?
j vais essayer d'implementer ca ce soir.
cs_aardman
Messages postés1905Date d'inscriptionmercredi 22 janvier 2003StatutMembreDernière intervention17 septembre 20123 15 déc. 2004 à 17:28
Salut,
Et oui ce sont exactement les meme fonctions, sauf qu'ils ont mis un WSA devant, en fait.
Pareil pour les events, les event winsock et les events window c'est exactement la meme chose.
Spiffou
Messages postés100Date d'inscriptionjeudi 1 avril 2004StatutMembreDernière intervention 9 juin 20141 15 déc. 2004 à 19:07
ok donc voila ce que ca devrait donner, là on verifie bien si il y a des evenements signalés pour chaque socket.
ps ca marche bien mais la gestion des events est elle bien ce qu'elle doit etre... (je me suis pas encore penché sur la fermeture du socket)
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=0; i < EventTotal ;i++)
{
int rc;
// on verifie si l'event est signalé
rc = WaitForSingleObject(EventArray[i], 0);
if (rc != WAIT_FAILED && rc != WAIT_TIMEOUT)
{
// si oui, on traite l'event
Index = i;
// 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();
Spiffou
Messages postés100Date d'inscriptionjeudi 1 avril 2004StatutMembreDernière intervention 9 juin 20141 15 déc. 2004 à 19:12
quoique là la gestion des events se fait bien sur plusieurs sockets, mais comme on ne gere pas le fait qu'il puisse y avoir plusieurs socket ca ne marche pas...
puisqu'on a qu'une seule var S_socket. il en faudrait plus ou alors allouer de la emoire à une structure de socket comme dans l'exple, mais bon dans mon cas, il ne peut y avoir qu'1 seule connexion donc j vais juste protéger la fonction accept pour pas qu'elle ouvre d'autre connexion. non?
DWORD ThreadId;
// On démarre le thread. ThreadClient est l'identifiant du thread
// ThreadId ne nous est d'aucune utilité dans cet exmple car le thread se termine de lui même
CreateThread(NULL,NULL,ThreadServeur,NULL,NULL,&ThreadId);
// WaitForSingleObject(hEvent,INFINITE);
// Boucle ppale du CHAT
while (strcmp(Sbuffer,"quit")!=0)
{
// saisie de la chaine
gets (Sbuffer);
// envoi de la chaine
send(S_socket, Sbuffer, strlen(Sbuffer)+1, 0);
}
// 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;
}
// Procédure utilisée par le thread
DWORD WINAPI ThreadServeur(LPVOID lpParam)
{
int k=0,j=0;
int nb_octets;
int Ret;
char buffer[250];
WSADATA WSAData;
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
// 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();
// mise à l'écoute de demande de connection client
// on utilise une file d'attente de 5 place
//------------------------------------------------
listen(L_socket, QUEUE);
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=0; i < EventTotal ;i++)
{
int rc;
// on verifie si l'event est signalé
rc = WaitForSingleObject(EventArray[i], 0);
if (rc != WAIT_FAILED && rc != WAIT_TIMEOUT)
{
// si oui, on traite l'event
Index = i;
// 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();
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;
}
}
if(NetworkEvent.lNetworkEvents & FD_CLOSE)
{
if (NetworkEvent.iErrorCode[FD_CLOSE_BIT] != 0)
{
printf("FD_CLOSE failed with error %d\n\n\a\n\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);
closesocket(SocketArray[Index]);
// Remove socket and associated event from
// the Socket and Event arrays and decrement
// EventTotal
// CompressArrays(EventArray, SocketArray, &EventTotal);
}
}
}
}
}
cs_aardman
Messages postés1905Date d'inscriptionmercredi 22 janvier 2003StatutMembreDernière intervention17 septembre 20123 17 déc. 2004 à 00:23
Salut,
Lors tu traitement du FD_READ:
recv(SocketArray[Index], ...);
au lieu de:
recv(S_socket, ...);
je vois pas trop ce que vient faire S_socket ici d'ailleur.
Lorsque l'event EventArray[Index] est a l'état signalé, c'est le socket SocketArray[Index] qui a généré cet evenement, pas un autre.
Quant a la fonction CompressArray:
Lors d'un FD_CLOSE, tu:
- ferme l'event
- ferme le socket
- met l'event a 0
- met le socket a 0
et ta fonction CompressArray devra juste a enlever tout les 0 des deux tableaux.