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

MFC Discussion :

Programme qui parait bloqué lors d'une connection au socket


Sujet :

MFC

  1. #1
    Membre régulier
    Profil pro
    Inscrit en
    Décembre 2005
    Messages
    227
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Décembre 2005
    Messages : 227
    Points : 121
    Points
    121
    Par défaut Programme qui parait bloqué lors d'une connection au socket
    Salut,

    Pour établir une connexion par sockets on est ammené à utiliser la fonction "connect()" or cette fonction bloque le programme (le temp de son execution peut prendre plusieurs secondes)
    Donc pendant ce temp les messages windows ne sont pas traité ce qui a pour conséquence de bloquer l'application (elle ne répond jusqu'a ce que la fonction "connect()" ai terminé son execution.

    Quelqu'un aurait t'il une solution pour remédier a ce blocage de l'appli ?

    Merci de m'aider

    PS: Le pire c'est quant on utilise connect() avec une IP introuvable/indisponible la ça bloque pendant 5sec.

  2. #2
    Expert éminent sénior
    Avatar de Médinoc
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Septembre 2005
    Messages
    27 379
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 40
    Localisation : France

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Septembre 2005
    Messages : 27 379
    Points : 41 573
    Points
    41 573
    Par défaut
    Ben, tu peux utiliser les sockets en asynchrone...

    Sous MFC, tu peux utiliser la classe CAsyncSocket. En mode Win32 normal, tu as le choix entre WSAAsyncSelect() (transformation d'événements socket en messages Windows, mais il faut avoir une fenêtre) ou WSAEventSelect() (permet d'utiliser WSAWaitForMultipleEvents(), qui équivaut à select() en plus puissant).

    Pour toi, je conseillerais WSAAsyncSelect().
    (voir sa doc, elle est assez facile à utiliser, encore plus pour un seul socket)

    PS:
    Par contre, tu peux avoir besoin de remettre le socket en non-bloquant ensuite, et ce n'est pas trivial.
    Voici une petite fonction qui le fait:
    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
    /* Fonction pour passer un socket en mode bloquant ou non
       ------------------------------------------------------ */
    BOOL RegleNonBloquant(SOCKET s, BOOL bNonBloquant)
    {
    BOOL bRetour;
    unsigned long ulBoolNonBloquant=(unsigned long)bNonBloquant;
    unsigned long ul, tul;
     
    //bRetour=(WSAAsyncSelect( s, NULL, WM_NULL, 0)==0);
    bRetour = (WSAEventSelect(s, 0, 0)==0);
    if(bRetour==FALSE)
    	{
     
    	// Erreur...
     
    	return FALSE;
    	}//if !bRetour
     
    bRetour = (WSAIoctl(
     s, //socket
     FIONBIO,    //mode bloquant/non-bloquant
     &ulBoolNonBloquant,    //Non-nul pour nb, nul pour bloquant
     sizeof(ulBoolNonBloquant), //Taille du paramètre d'entrée
     &ul,  //buffer de sortie
     0, //Taille du buffer de sortie
     &tul,  //Pointeur pour retour taille de la valeur sortie
     NULL,  //LPOVERLAPPED: Je ne sais pas ce que ç'est, mais c'est NULL
     NULL   //Sais pas ce que c'est non plus, mais c'est NULL aussi.
     )==0);
    if(bRetour==FALSE)
    	{
     
    	//Erreur...
     
    	}//if !bRetour
    return bRetour;
    }

  3. #3
    Membre régulier
    Profil pro
    Inscrit en
    Décembre 2005
    Messages
    227
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Décembre 2005
    Messages : 227
    Points : 121
    Points
    121
    Par défaut
    Euh... j'ai pas bien compris les 2 derniers paramètres et c'est ou qu'on spécifie l'adresse IP et le Port pour se connecter ?

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    int WSAAsyncSelect(SOCKET s, HWND hWnd, unsigned int wMsg, long lEvent);
    et aussi pourquoi on peut pas utiliser ça ensuite pour mettre le socket en mode non-bloquant :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    u_long argp=1;
    ioctlsocket(sock, FIONBIO, &argp);
    PS: Dsl je commence la programmation en Visual et j'ai un peu du mal

  4. #4
    Expert éminent sénior
    Avatar de Médinoc
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Septembre 2005
    Messages
    27 379
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 40
    Localisation : France

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Septembre 2005
    Messages : 27 379
    Points : 41 573
    Points
    41 573
    Par défaut
    WSAAsyncSelect() ne sert pas à se connecter:
    Cette fonction règle le socket en mode non-bloquant et sert à être prévenu quand la connexion est établie.

    Petit exemple (tiré d'un programe à moi):
    Ici, la fenêtre possède quelque part des données en mémoire (variable globale, statique, ou plus compliqué) référencées par ma_mem (pointeur de structure)
    L'emplacement ma_mem->ma_mem->st_client.socket_client est là où le socket est mémorisé.
    Ce code est appelé par exemple quand on clique sur un bouton. Il:
    • crée le socket
    • appelle WSAAsyncSelect() pour dire "Dès qu'on réussira à se connecter, ou qu'il y aura des données à recevoir, ou qu'il faudra fermer le socket, cette fenêtre recevra un message WMSG04_SOCKET04" (un message perso: Je l'ai défini à (WM_USER+100) comme indiqué dans la doc).
    • exécute connect() pour dire de lancer la connexion (mais n'attend pas que celle-ci soit établie).
      Remarque: La structure sin Est la structure SOCKADDR_IN dans laquelle on place l'adresse IP et le n° de port. Son remplissage n'est pas montré ici, disons qu'il a été fait avant.
    • Puis, on rend le contrôle

    NOTE: La fonction puts_FenetreConsole() est une fonction à moi, qui me permet d'afficher un message dans une fenêtre perso. C'est trop gros pour être posté ici, mais tu peux afficher autrement (Dans une editbox, par exemple, mais PAS dans une MessageBox: les MessageBox ont une facheuse tendance à "foutre le bordel" si tu me permets l'expression)
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    //Crée le socket
    ma_mem->st_client.socket_client  = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
    if(ma_mem->st_client.socket_client != INVALID_SOCKET)
    	{
    	WSAAsyncSelect(ma_mem->st_client.socket_client, hWnd, WMSG04_SOCKET04, FD_CONNECT | FD_READ | FD_CLOSE);
    	connect(ma_mem->st_client.socket_client,(SOCKADDR *)&sin,sizeof(SOCKADDR_IN));
    	puts_FenetreConsole(hCon,"Attente du résultat de la connexion...",true);
    	bOK=true;
    	}
    else
    	{
    	puts_FenetreConsole(hCon,"Echec de l'initialisation de la connexion.",true);
    	}
    Ici, c'est le code de traitement du message WMSG04_SOCKET04 dans la procédure de fenêtre.
    Là, on introduit en plus une nouvelle variable, ma_mem->bConnecte qui est mise à true lorsque la connexion est établie.
    On peut voir aussi un petit inconvénient, il arrive que l'on reçoive plusieurs fois la notification FD_CLOSE: Le fait de mémoriser le fait d'être connecté ou non permet de ne l'afficher qu'une fois.
    En dehors de ça, le traitement de chaque événement n'est pas compliqué...
    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
    case WMSG04_SOCKET04:
    	switch(WSAGETSELECTEVENT(lParam))
    		{
    		int erreur;
    		case FD_CONNECT:
    			erreur = WSAGETSELECTERROR(lParam);
    			//Si pas d'erreur, connexion établie.
    			if(erreur==0)
    				{
    				puts_FenetreConsole(hCon, "-- Connexion établie. --", true);
    				ma_mem->bConnecte = true;
    				}
    			else
    				{
    				puts_FenetreConsole(hCon, "-- Echec de Connexion. --", true);
    				}
    			break;
     
    		case FD_READ:
    			erreur = WSAGETSELECTERROR(lParam);
     
    			//Ici, on reçoit les données avec recv(), etc.
     
    			break;
     
    		case FD_CLOSE:
    			if(ma_mem->bConnecte)
    				{
    				puts_FenetreConsole(hCon, "Connexion fermée par le serveur.", true);
    				//ferme le socket de travail
    				closesocket(ma_mem->st_client.socket_client);
    				ma_mem->bConnecte = false;
    				}
    			else
    				puts_FenetreConsole(hCon, "re-Connexion fermée par le serveur.", true);
    			break;
    		}//switch WSAGETSELECTEVENT
    	break;
    Bien sûr, le code montre ici une gestion entièrement en mode non-bloquant.
    Mais si tu le souhaites, tu peux repasser le socket en mode bloquant une fois la connexion établie, rien ne t'en empêche...

  5. #5
    Membre régulier
    Profil pro
    Inscrit en
    Décembre 2005
    Messages
    227
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Décembre 2005
    Messages : 227
    Points : 121
    Points
    121
    Par défaut
    Ok merci je vais essayer ça je te tien au courant

    sinon pour passer en mode non-bloquant je peut touours utiliser de la même façcon qu'avant ? C'est a dire comme ça :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    u_long argp=1; 
    ioctlsocket(sock, FIONBIO, &argp);

  6. #6
    Membre régulier
    Profil pro
    Inscrit en
    Décembre 2005
    Messages
    227
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Décembre 2005
    Messages : 227
    Points : 121
    Points
    121
    Par défaut
    C'est bon ça marche merci a toi Médinoc,

    Par contre j'ai un petit problème avec "FD_READ", je reçoit chaque message en double.

    Ca fait comme si le message restait dans le tampon de reception lors de la première lecture.

    portant j'ai bien mis 0 au dernier paramètre de recv() (voir ci dessous)

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    //reception du 1er caractère qui contien la taille du message
    recv(sock,buffer,1,0);
     
    //"char_to_int()" converti le code ascii (de -127 à 128) en int (de 0 à 255)
    int taille_a_recevoir=char_to_int(buffer[0]);
     
    //reception du message
    recv(sock,buffer,taille_a_recevoir,0);
    PS: si on est en mode bloquant ça pose pas de problème non : Vu que le message "FD_READ" est envoyé que si il y a un nouveau message dans le tampon de reception. Je me trompe : (a condition bien sur que l'on utilise recv() uniquement dans "case FD_READ:")

  7. #7
    Membre régulier
    Profil pro
    Inscrit en
    Décembre 2005
    Messages
    227
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Décembre 2005
    Messages : 227
    Points : 121
    Points
    121
    Par défaut
    En relisant msdn plus attentivement j'ai compris mon erreur :
    http://msdn.microsoft.com/library/de...ncselect_2.asp

    En fait dans la "case FD_READ:" on ne peut faire qu'un seul appel a "recv();", si on en fait plusieurs alors on recevra plusieurs messages "FD_READ"

    Désolé pour le dérangement


  8. #8
    Expert éminent sénior
    Avatar de Médinoc
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Septembre 2005
    Messages
    27 379
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 40
    Localisation : France

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Septembre 2005
    Messages : 27 379
    Points : 41 573
    Points
    41 573
    Par défaut
    Généralement, je fais mes lectures de la même façon: Je désactive tout simplement FD_READ avant la première lecture (WSAAsyncSelect(s, hWnd, message, FD_CLOSE); plus besoin de FD_CONNECT) et le réactive après la seconde (WSAAsyncSelect(s, hWnd, message, FD_READ | FD_CLOSE).

    PS: WSAAsyncSelect() règle tout seul le socket en mode non-bloquant.
    Par contre, pour le replacer en mode bloquant, on ne peut pas faire le FIONBIO directement: Il faut supprimer toutes les notifications d'abord (en appelant WSAAsyncSelect() ou WSAEventSelect() avec 0, comme mon code le fait)

  9. #9
    Membre régulier
    Profil pro
    Inscrit en
    Décembre 2005
    Messages
    227
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Décembre 2005
    Messages : 227
    Points : 121
    Points
    121
    Par défaut
    Je cherche peut être des bugs la ou il peut pas y en avoir mais,
    si un message arrvie sur la socket pendant que WSAAsyncSelect(..., FD_READ); est desactivé on ne sera pas prevenu de son arrivé... que ce passe t'il :


  10. #10
    Expert éminent sénior
    Avatar de Médinoc
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Septembre 2005
    Messages
    27 379
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 40
    Localisation : France

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Septembre 2005
    Messages : 27 379
    Points : 41 573
    Points
    41 573
    Par défaut
    On sera prévenu quand on activera de nouveau FD_READ, car il y aura des données dans le buffer de réception.
    Donc non, il n'y a pas de bug de ce coté-là.

  11. #11
    Membre régulier
    Profil pro
    Inscrit en
    Décembre 2005
    Messages
    227
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Décembre 2005
    Messages : 227
    Points : 121
    Points
    121
    Par défaut
    Ok merci encore une question, vous allez dire il s'arrête plus celui la...
    cette fois je pense que c'est la dernière :
    Pour diverses raisons j'aurait besoin de poster un message FD_CLOSE j'ai fait comme ça et ça marche pas
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    SendMessage(hwnd, WM_USER, FALSE, (LPARAM)FD_CLOSE);
    PS: j'ai passé WM_USER en paramètre dans "WSAAsyncSelect();"

  12. #12
    Expert éminent sénior
    Avatar de Médinoc
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Septembre 2005
    Messages
    27 379
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 40
    Localisation : France

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Septembre 2005
    Messages : 27 379
    Points : 41 573
    Points
    41 573
    Par défaut
    Je ne crois pas que ce soit fait pour marcher.
    Que cherches-tu à faire exactement ?
    Le message FD_CLOSE ne ferme pas lui-même le socket, il dit seulement que ta fonction doit le fermer.

    Tu peux encapsuler le traitement de FD_CLOSE dans une fonction, et appeler cette fonction ensuite...



    De plus, wParam est le n° de socket, pas FALSE.

    PS: Si tu utilises des boites de dialogue, n'utilise pas WM_USER: les boites de dialogues utilisent quelques messages dans cette zone.
    C'est pourquoi la doc conseille de commencer à WM_USER+100 (je ne sais plus trop où dans la doc, mais je l'avais vu qq part...)

  13. #13
    Membre régulier
    Profil pro
    Inscrit en
    Décembre 2005
    Messages
    227
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Décembre 2005
    Messages : 227
    Points : 121
    Points
    121
    Par défaut
    Citation Envoyé par Médinoc
    Le message FD_CLOSE ne ferme pas lui-même le socket, il dit seulement que ta fonction doit le fermer.
    Oui je sais, en fait ce que je veut faire c'est un peu compliqué a expliquer, dans la "case FD_CLOSE:" j'ai un "if()" dont le socket n'est pas forcément fermé losrque FD_CLOSE est reçu. Une fois que la condition du if() est OK il me faut repasser par FD_CLOSE pour fermer le socket.

    Citation Envoyé par Médinoc
    Tu peux encapsuler le traitement de FD_CLOSE dans une fonction, et appeler cette fonction ensuite...
    C'est vrai que c'est une autre solution, mais j'utilise pas mal de variables dans la "case FD_CLOSE:" et comme j'aime pas trop utiliser les variables globales je serais obligé de toutes les passer en paramètre, ça fait un peu lourd....

    Citation Envoyé par Médinoc
    De plus, wParam est le n° de socket, pas FALSE.
    PS: Si tu utilises des boites de dialogue, n'utilise pas WM_USER: les boites de dialogues utilisent quelques messages dans cette zone.
    Je savais pas, du coup j'ai mis WM_USER+100 comme tu m'a conseillé. Pour le numero du socket je savais pas aussi, j'ai changé ça et ça marche
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    SendMessage(hwnd, WM_USER+100, sock, FD_CLOSE);
    Merci beaucou a toi Médinoc

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. Réponses: 1
    Dernier message: 22/10/2008, 16h14
  2. Réponses: 8
    Dernier message: 04/03/2008, 23h30
  3. champs qui s'efface lors d'une requete ajax.
    Par starr dans le forum Langage
    Réponses: 3
    Dernier message: 27/10/2006, 08h33
  4. Réponses: 2
    Dernier message: 05/07/2006, 15h21
  5. Formulaire bloqué lors d'une ouverture par macro.
    Par Monsieur Peck dans le forum IHM
    Réponses: 6
    Dernier message: 16/06/2006, 17h41

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