Problème avec WSAGetLastError()

cs_fredo2009 Messages postés 17 Date d'inscription lundi 20 novembre 2000 Statut Membre Dernière intervention 5 juillet 2006 - 1 juil. 2006 à 04:13
cs_fredo2009 Messages postés 17 Date d'inscription lundi 20 novembre 2000 Statut Membre Dernière intervention 5 juillet 2006 - 5 juil. 2006 à 09:49
Bonjour @ tous

Je suis en train de coder un scanner multithread avec socket non bloquant.
Mon soucis est de récupérer le message d'erreur résultant du connect: si la connexion a réussi ou bien si elle a été refusée ou bien fermer le socket qd le timeout est dépassé.

En mode bloquant , je récupère bien le GetLastError() mais je ne peux pas régler le timeout du socket:
if(connect(mysock,(struct sockaddr *) & server,sizeof(server))!=0)
  {
  code=GetLastError();


  if(code==10061)
  {printf("la connexion sur le port %d a ete refusee \n",port);}


  }
    else
    {
    printf("la connexion sur le port %d a reussie\n",port);
    }

en mode non bloquant, select me renvoie la même erreur 0 pour le time out dépassé et la connexion refusée. Et pour la connexion réussie je reçois l'erreur 10035.

connect(conf->sock, (SOCKADDR *)&(conf->sin), sizeof(conf->sin));


          int codeRetour = select(0,&socketToCheck,&socketToCheck,&socketToCheck,&tempo);


                    conf->code=GetLastError();
                    if (conf->code==10061)
                    {
                    printf("la connexion sur le port %d a ete refusee \n",port);                      
                    closesocket(conf->sock);                        
                    shutdown(conf->sock,2); 
                    }
                    else 
                    {
                    printf("la connexion sur le port %d a reussie\n",port);             
                    closesocket(conf->sock);                        
                    shutdown(conf->sock,2); 
                    }

4 réponses

excrt Messages postés 75 Date d'inscription mercredi 5 avril 2006 Statut Membre Dernière intervention 3 juillet 2006
1 juil. 2006 à 21:00
tu ne peux pas envoyer le même « fd_set » aux trois « fd_set » que select() attent.
select() modifie les « fd_set » qu'il recoit. en passant, toi tu utilises « GetLastError() » et non « WSAGetLastError() »

//fd_set fdsread; // inutile pour un connect()
fd_set fdswrite;
fd_set fdserror;
timeval timeout;
int retval;
unsigned long noblock = 1;

//ref::http://msdn.microsoft.com/library/default.asp?url=/library/en-us/winsock/winsock/ioctlsocket_2.asp
    ioctlsocket(conf->sock, FIONBIO, &noblock);

    // initialize les  « fd_set »
    FD_ZERO(&fdswrite);
    FD_ZERO(&fdserror);

    // ajoute notre socket
    FD_SET(conf->sock, &fdswrite);
    FD_SET(conf->sock, &fdserror);

    // le timeout
    timeout.tv_sec = X;

    timeout.tv_msec = Y;

    // connect( ... );

ref::http://msdn.microsoft.com/library/default.asp?url=/library/en-us/winsock/winsock/select_2.asp
    retval = select(0, NULL, &fdswrite, &fdserror, &timeout);

    if (retval > 0)
    {
        if ( FD_ISSET(conf->sock, &fdswrite) ) {
            // ready
        }
        else if ( FD_ISSET(conf->sock, &fdserror) ) {
            printf("socket error: %d\n", WSAGetLastError());
        }
    }
    else if (retval == 0) {
        puts("timeout");
    }
    else if (retval == SOCKET_ERROR) {
        printf("select() error: %d\n", WSAGetLastError());
    }

pour les codes/messages d'erreurs:
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/debug/base/system_error_codes.asp

-=-= ExCRT =-=-
0
cs_fredo2009 Messages postés 17 Date d'inscription lundi 20 novembre 2000 Statut Membre Dernière intervention 5 juillet 2006
3 juil. 2006 à 03:03
C vrai que c plus propre comme ça.
Mais le résultat n'est pas encore correct.

Par ex je devrais obtenir qque chose comme ceci:

port 110 occupe  WSAGetLastError = 0 //la connexion a reussie sur ce port
port 111 ferme WSAGetLastError = 10060 //timeout
port 112 ferme WSAGetLastError = 10060 //timeout
port 113 libre WSAGetLastError = 10061 //la connexion a ete expressement refusee

Et au lieu de ça j'obtiens:
port 110 occupe WSAGetLastError = 10035
port 111 timeout WSAGetLastError = 0
port 112 timeout WSAGetLastError = 0
port 113 libre WSAGetLastError = 0
port 114 timeout WSAGetLastError = 0
port 115 timeout WSAGetLastError = 0
port 116 select() error = 10038
port 117 timeout WSAGetLastError = 0
port 141 select() error =  10038

Je n'ai donc pas les messages d'erreur attendu.
De plus le code erreur 10038 arrive aléatoirement. Je peux scanner 2 fois le même port, il ne sortira pas à chaque fois cette erreur.
Et si j'augmente le nbre de thread à plus de 9, le port 113 apparait en timeout.

Voilà le code complet :

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


struct config
 {
 int sock;
 sockaddr_in sin;
 int ip;
 int port;
 int code;
 HANDLE hthread;
 MSG msg;
 FILE * fic; //pour enregistrer ds un fichier les résultats. Qd le code sera au point ^^
 };


DWORD WINAPI connection( LPVOID Param )
 {
 struct config * conf = (struct config *)Param;
 WSADATA WSAData;
 WSAStartup(MAKEWORD(2,2), &WSAData);
 conf->sock = socket(PF_INET,SOCK_STREAM,0);
 conf->sin.sin_addr.s_addr = ntohl(conf->ip);
 conf->sin.sin_family = AF_INET;
 conf->sin.sin_port = ntohs(conf->port);
   
          //Passage de la socket en mode non bloquant
          unsigned long optVal=1;
          ioctlsocket(conf->sock,FIONBIO,&optVal);
      
          //fd_set fdsread; // inutile pour un connect()
          fd_set fdswrite;
          fd_set fdserror;


          timeval tempo;
          tempo.tv_sec = 2; //en s
          tempo.tv_usec = 0; //en ms
          // initialize les  « fd_set »
          FD_ZERO(&fdswrite);
          FD_ZERO(&fdserror);
          // ajoute notre socket
          FD_SET(conf->sock, &fdswrite);
          FD_SET(conf->sock, &fdserror);
         
          connect(conf->sock, (SOCKADDR *)&(conf->sin), sizeof(conf->sin));


    int retval = select(0, NULL, &fdswrite, &fdserror, &tempo);
    if (retval > 0)
    {
        if ( FD_ISSET(conf->sock, &fdswrite) )
        {
         printf ("port %d occupe WSAGetLastError %d\n",conf->port, WSAGetLastError()); 
            // ready
        }
        else if ( FD_ISSET(conf->sock, &fdserror) ) {
            printf("port %d libre WSAGetLastError %d\n",conf->port, WSAGetLastError());
        }
    }
    else if (retval == 0) {
        printf("port %d timeout WSAGetLastError %d\n",conf->port,WSAGetLastError());
    }
    else if (retval == SOCKET_ERROR) {
         //if (WSAGetLastError()!=10038)
        {
            printf("port %d select() error: %d\n",conf->port, WSAGetLastError());
        }
    }
              //on repasse en mode bloquant
              optVal = 0;
              ioctlsocket (conf->sock, FIONBIO, &optVal);
   
    shutdown(conf->sock,2);
    closesocket(conf->sock);
 WSACleanup();
 free(conf);
 return 0;
 }
   
int main(int argc, char *argv[])
 {
 if ( argc == 4)
    { 
       struct config * conf = NULL;
       int nbrethread;
       int nthread;
       int portascanner;
       int scanip;
       portascanner = atoi(argv[1]);
    nbrethread = atoi(argv[2]);
    scanip = htonl(inet_addr(argv[3]));
       SetTimer(NULL,NULL,2000,NULL);
         for (;;)
   {  
             nthread = nbrethread;
               
                for (;nthread>0;nthread--)
    {
                conf = (struct config *) malloc(sizeof(struct config) );
                conf->ip = scanip;
                conf->port=portascanner;
    conf->hthread = CreateThread(NULL, 0, &connection, conf, 0, 0);
    CloseHandle(conf->hthread);
    portascanner++;
    }
    
    //ptite tempo de 2s entre chaque série de thread
                GetMessage(&conf->msg,NULL,0,0);
                while (conf->msg.message!=WM_TIMER)
                {;;}
                CloseHandle(conf->hthread);
   }
    KillTimer(NULL,NULL);
       return 0;
     }
 else
  {
  printf("Usage : scan.exe port_debut nthread ip\n");
  return 0;
 
  }
 }




 
0
excrt Messages postés 75 Date d'inscription mercredi 5 avril 2006 Statut Membre Dernière intervention 3 juillet 2006
3 juil. 2006 à 16:59
WSAStartup() tu dois l'appeler « une fois par _Process_ », pas par thread ...

int main()
{
    // ...
    WSAStartup(..., ...);

    // ...

    WSACleanup();
    return 0;
}

après l'appel a select(), si ton socket est présent dans « fdswrite » c'est qu'il est pret pour l'envoie de données, donc, connexion réussi, pas d'échec. s'il est présent dans fdserror c'est qu'il y a erreur, donc la connexion a probablement échouée ...

tu peux récupérer le code d'erreur associé a chacun des sockets via « getsockopt() »

int getSocketError(SOCKET sock)
{
    int error_code;
    int size = sizeof(int);

    if (getsockopt(sock, SOL_SOCKET, SO_ERROR, &error_code, &size) != SOCKET_ERROR) {
        return error_code;
    }
    return WSAGetLastError();
}

    if ( FD_ISSET(conf->sock, &fdserror) ) {
        printf("socket error %d\n", getSocketError(conf->sock));
    }

pour tes « include »

#pragma comment(lib, "ws2_32.lib") // fait gaffe, ce « #pragma » ne fonctionne qu'en C++(tu compile en C++ présentement, va dans la config et change ca pour une compilation en C)
#include <winsock2.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <string.h>

//#include <winbase.h> // <winsock2.h> inclut <windows.h> qui lui inclut <winbase.h> et d'autres fichiers

ton main() est ... bizarre ...

int main(int argc, char *argv[])
 {
 if ( argc == 4)
    { 
       struct config * conf = NULL;
       int nbrethread;
       int nthread;
       int portascanner;
       int scanip;
       portascanner = atoi(argv[1]);
    nbrethread = atoi(argv[2]);
    scanip = htonl(inet_addr(argv[3]));
       SetTimer(NULL,NULL,2000,NULL); // why?
         for (;;)
   {  
             nthread = nbrethread;
               
                for (;nthread>0;nthread--)
    {
                // ici, tu as un malloc() mais sans free() plus bas ... Problème!
                // c'est dans ton main() que tu devrais faire appel a free()
                // et non dans le thread
                conf = (struct config *) malloc(sizeof(struct config) );
                conf->ip = scanip;
                conf->port=portascanner;
    conf->hthread = CreateThread(NULL, 0, &connection, conf, 0, 0);
    CloseHandle(conf->hthread); // pourquoi fermes-tu le thread immédiatement après l'avoir créé???
    portascanner++;
    }
   
    // Ca ce n'est pas bon, mais pas du tout ...

    //ptite tempo de 2s entre chaque série de thread
                //GetMessage(&conf->msg,NULL,0,0);
                // si « conf->msg.message » ne vaut pas WM_TIMER
                // tu boucleras infiniment
                //while (conf->msg.message!=WM_TIMER)
                //{;;}
                while (GetMessage(&(conf->msg), NULL, 0, 0))
                {
                    if (conf->msg.message == WM_TIMER) {
                        break;
                    }
                }
                CloseHandle(conf->hthread);
                free(conf); // <<==
   }
    KillTimer(NULL,NULL);
       //return 0;
     }
 else
  {
  printf("Usage : scan.exe port_debut nthread ip\n");
  //return 0;
   }
   return 0;
 }

pourquoi utilises-tu SetTimer()/GetMessage()/etc.? si tu veux faire une pause de X [milli]sec, utilise « VOID Sleep(DWORD dwMilliSec); »

Sleep( 2000 ); // aussi simple que ca ..

si tu veux lancer plusieurs threads avec différentes informations(struct config), utilise ceci:

#define MAX_THREAD  MAXIMUM_WAIT_OBJECTS

    HANDLE hThreads[MAX_THREAD];
    struct config* confs = (struct config*)malloc(MAX_THREAD * sizeof(struct config));

    for (i = 0; i < MAX_THREAD; i++)
    {
        confs[i].x = x_value;
        confs[i].y = y_value;
        confs[i].z = z_value;
        // etc.

        hThreads[i] = CreateThread(NULL, 0, connection, &(confs[i]), 0, 0);
    }

    // on attend les threads
    WaitForMultipleObjects(MAX_THREAD, hThreads, TRUE, INFINITE);

    // fin des threads
    free(confs);

etc.
etc.
etc.

-=-= ExCRT =-=-
0
cs_fredo2009 Messages postés 17 Date d'inscription lundi 20 novembre 2000 Statut Membre Dernière intervention 5 juillet 2006
5 juil. 2006 à 09:49
Merci excrt , c vraiment un cours que tu m'as fait là.

pourquoi utilises-tu SetTimer()/GetMessage()/etc.?
j'avais commencé par un Sleep(2000) et puis j'ai lu sur qques forum que Sleep avait tendance à endorpmir tous le programme. J'ai donc cherché autre chose pour faire une tempo. Mais finalement ça n change rien, je remet donc le Sleep.

#pragma comment(lib, "ws2_32.lib") // fait gaffe, ce « #pragma ».....
En fait ce pragma ne me sert à rien vu que je compile sous dev. Il me suffit de rajouter ds l'editeur de lien -lwsock32. D'après ce que j'ai compris il aurait servi pour une compilation sous visual.

Enfin le prog commence à tourner un peu plus rond. Il fonctionne sans bug jusqu'à 10 thread simultannés. Au-delà, il ne détecte plus les connexions refusées mais seulement les connexions réussies.
0
Rejoignez-nous