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

Windows Discussion :

Sockets dans threads


Sujet :

Windows

  1. #1
    Membre averti Avatar de Trunks
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Mai 2004
    Messages
    534
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 41
    Localisation : France, Val de Marne (Île de France)

    Informations professionnelles :
    Activité : Développeur informatique

    Informations forums :
    Inscription : Mai 2004
    Messages : 534
    Points : 412
    Points
    412
    Par défaut Sockets dans threads
    Bonjour,

    J'ai une liste de clients qui doivent se connecter sur une liste de serveurs.
    J'ai créé 2 exécutables, un pour lancer une multitude de clients dans des threads parallèles, et un pour le serveur. Le code est simplifié, donc c'est normal qu'il manque des contrôles. Voici les codes sources :

    Serveur

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    #include <stdio.h>
    #include <winsock2.h>
    
    #pragma comment(lib, "Ws2_32.lib")
    
    int main()
    {
        WSADATA wsaData;
    
        if (WSAStartup(MAKEWORD(2, 0), &wsaData) != 0)
            fprintf(stderr, "La fonction WSAStartup a echoue.\n");
        else
        {
            SOCKET s_server;
            
            s_server = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
            if (s_server == INVALID_SOCKET)
                fprintf(stderr, "La fonction socket a echoue.\n");
            else
            {
                SOCKADDR_IN server;
                
                server.sin_family       = AF_INET;
                server.sin_addr.s_addr  = htonl(INADDR_ANY);
                server.sin_port         = htons(4999);
                memset(&server.sin_zero, '\0', sizeof(server.sin_zero));
                
                if (bind(s_server, (SOCKADDR *)&server, sizeof(server)) == SOCKET_ERROR)
                    fprintf(stderr, "La fonction bind a echoue.\n");
                else
                {
                    if (listen(s_server, 0) == SOCKET_ERROR) /* listen : commencer l'ecoute */
                        fprintf(stderr, "La fonction listen a echoue.\n");
                    else
                    {
    					while (true)
    					{
    						SOCKET s_client;
    						SOCKADDR_IN client;
    						int csize = sizeof(client);
                        
    						s_client = accept(s_server, (SOCKADDR *)&client, &csize);
                        
    						if (s_client == INVALID_SOCKET)
    							fprintf(stderr, "La fonction accept a echoue.\n");
    						else
    						{
    							char buffer[100];
    							int n;
                            
    							printf("Le client %s s'est connecte.\n", inet_ntoa(client.sin_addr));
                            
    							strcpy(buffer, "Bonjour\n");
    							send(s_client, buffer, (int)strlen(buffer), 0);
                            
    							n = recv(s_client, buffer, sizeof(buffer) - 1, 0);
                            
    							if (n != SOCKET_ERROR)
    							{
    								buffer[n] = '\0';
    								printf("%s", buffer);
    							}
    
    							closesocket(s_client);
    						}
    					}
                    }
                }
                
                closesocket(s_server);
            }
            
            WSACleanup();
        }
        
        return 0;
    }

    Clients

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    #include <winsock2.h>
    #include <stdio.h>
    
    #define NB_SOCKS_MAX 10
    
    #pragma comment(lib, "Ws2_32.lib")
    
    void WithPool();
    void CreatePoolThreads(int iIndexMin, int iIndexMax);
    int ConnectToServer(char * lpszServer, int iPort);
    DWORD WINAPI StartThread(LPVOID lpParam);
    
    int main()
    {
    	while (true)
    	{
    		WithPool();
    	}
    
    	return EXIT_SUCCESS;
    }
    
    void WithPool()
    {
    	CreatePoolThreads(115, 130);
    }
    
    void CreatePoolThreads(int iIndexMin, int iIndexMax)
    {
    	HANDLE achHThread[NB_SOCKS_MAX] = { NULL };
    	int iIndex = iIndexMin;
    
    	while (iIndex <= iIndexMax)
    	{
    		int iPoolCount = (iIndex + NB_SOCKS_MAX - 1 <= iIndexMax) ? NB_SOCKS_MAX : iIndexMax - iIndex + 1;
    		int i;
    
    		for (i = 0; i < iPoolCount; ++i)
    		{
    			achHThread[i] = CreateThread(NULL, 0, static_cast<LPTHREAD_START_ROUTINE>(StartThread), &iIndex, 0, NULL);
    			
    			// Pour laisser le temps au thread de récupérer la valeur de lpParameter
    			Sleep(100);
    
    			++iIndex;
    		}
    		
    		WaitForMultipleObjects(iPoolCount, achHThread, TRUE, INFINITE);
    
    		for (i = 0; i < NB_SOCKS_MAX; ++i)
    		{
    			if (achHThread[i] && (achHThread[i] != INVALID_HANDLE_VALUE))
    			{
    				CloseHandle(achHThread[i]);
    				achHThread[i] = NULL;
    			}
    		}
    
    		Sleep(3000);
    	}
    }
    
    int ConnectToServer(char * lpszServer, int iPort)
    {
    	fprintf(stdout, "Connexion au serveur %s.\n", lpszServer);
    
        WSADATA wsaData;
    
        if (WSAStartup(MAKEWORD(2, 0), &wsaData) != 0)
    	{
            fprintf(stderr, "La fonction WSAStartup a echoue.\n");
    		return -1;
    	}
        else
        {
            SOCKET s;
            
            s = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
            
    		if (s == INVALID_SOCKET)
    		{
                fprintf(stderr, "La fonction socket a echoue (%s).\n", lpszServer);
    			return -1;
    		}
            else
            {
                SOCKADDR_IN server;
    			ULONG fionbio;
                
                server.sin_family       = AF_INET;
                server.sin_addr.s_addr  = inet_addr(lpszServer);
                server.sin_port         = htons(iPort);
                memset(&server.sin_zero, '\0', sizeof(server.sin_zero));
    
    			// Mode non-bloquant
    			fionbio = TRUE;
    			ioctlsocket(s, FIONBIO, &fionbio);
    
    			if ((connect(s, (SOCKADDR *)&server, sizeof(server))) == SOCKET_ERROR)
    			{		
    				fd_set writefds;
    				struct timeval tv;
    						
    				tv.tv_sec = 5;
    				tv.tv_usec = 0; 
    
    				FD_ZERO(&writefds);
    				FD_SET(s, &writefds);
    				select(0, NULL, &writefds, NULL, &tv);
    
    				// Retour au mode bloquant
    				fionbio = FALSE;
    				ioctlsocket(s, FIONBIO, &fionbio);
    
    				if (FD_ISSET(s, &writefds))
    				{
    					fprintf(stdout, "%s connecte.\n", lpszServer);
    
    					char buffer[100];
    					int n;
                
    					n = recv(s, buffer, sizeof(buffer) - 1, 0);
                
    					if (n != SOCKET_ERROR)
    					{
    						buffer[n] = '\0';
    						printf("%s", buffer);
                    
    						send(s, "Au revoir\n", (int)strlen("Au revoir\n"), 0);
    					}
                
    					shutdown(s, SD_SEND);
    				}
    				else
    				{
    					fprintf(stdout, "%s non connecte.\n", lpszServer);
    				}
    			}
                
                closesocket(s);
            }
            
            WSACleanup();
        }
    
    	return 0;
    }
    
    DWORD WINAPI StartThread(LPVOID lpParam)
    {
    	char achServer[14];
    	int iCr;
    
    	int * lpNum = reinterpret_cast<int *>(lpParam);
    	int i = *lpNum;
    
    	sprintf(achServer, "%d.0.0.1", i);
    
    	iCr = ConnectToServer(achServer, 4999);
    
    	return 0;
    }
    Le client se connecte au serveur via un socket. Je fais un connect de façon asynchrone afin de pour spécifier un timeout de connexion (va select).

    Donc je crée un serveur en localhost, et ensuite je lance 20 clients dans des threads parallèles. J'ai créé des pools de threads car j'ai remarqué qu'on pouvait lancé un nombre limité de sockets simultanées (env 10 lors d'un test mais que je n'arrive plus à reproduire). Les clients vont se connecter à des serveurs différents, dans mon test, j'en ai mis qu'un actif, donc normalement 19 clients n'arriveront pas à joindre leur serveur, et seul un réussira (127.0.0.1).

    Toutefois, si j'ai des pools trop grosses de threads (> 6), le client n'arrive pas toujours à se connecter sur le serveur local (paramétrage: NB_SOCKS_MAX => 7, CreatePoolThreads(116, 130)). Et je n'arrive pas à comprendre pourquoi. Est-ce un problème de socket ou autre chose?

    Merci d'avance

  2. #2
    Inactif  
    Avatar de Mac LAK
    Profil pro
    Inscrit en
    Octobre 2004
    Messages
    3 893
    Détails du profil
    Informations personnelles :
    Âge : 49
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations forums :
    Inscription : Octobre 2004
    Messages : 3 893
    Points : 4 846
    Points
    4 846
    Par défaut
    Côté généralités : Normalement, avec WinSock 2, le champ iMaxSockets n'a plus de sens (cf. cette page). Peut-être devrais-tu quand même vérifier ce point lors de ton appel à WSAStartup, notamment en changeant les diverses versions demandées.
    Sinon, regarde du côté de cette page pour vérifier que tu n'aie pas une limitation via FD_SETSIZE... Pour ce que j'ai pu voir sur le reste (ex : limite en nombre de fichiers / handles ouverts), tous les chiffres par défaut explosent de très loin ta limite actuelle (ex : 512 fichiers simultanés au minimum pour les accès stdio), il est donc peu probable que ce soit en dehors de Winsock.


    Du côté de ton code : tu es certain que ton while (true) { WithPool(); } ne crée pas une "infinité" de threads, donc de sockets, et donc sature Winsock ? Je n'ai pas testé ni compilé ton code, peut-être as-tu protégé derrière, mais ça n'en donne pas l'impression...
    Mac LAK.
    ___________________________________________________
    Ne prenez pas la vie trop au sérieux, de toutes façons, vous n'en sortirez pas vivant.

    Sources et composants Delphi sur mon site, L'antre du Lak.
    Pas de question technique par MP : posez-la dans un nouveau sujet, sur le forum adéquat.

    Rejoignez-nous sur : Serveur de fichiers [NAS] Le Tableau de bord projets Le groupe de travail ICMO

  3. #3
    Membre averti Avatar de Trunks
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Mai 2004
    Messages
    534
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 41
    Localisation : France, Val de Marne (Île de France)

    Informations professionnelles :
    Activité : Développeur informatique

    Informations forums :
    Inscription : Mai 2004
    Messages : 534
    Points : 412
    Points
    412
    Par défaut
    Citation Envoyé par Mac LAK Voir le message
    Côté généralités : Normalement, avec WinSock 2, le champ iMaxSockets n'a plus de sens (cf. cette page). Peut-être devrais-tu quand même vérifier ce point lors de ton appel à WSAStartup, notamment en changeant les diverses versions demandées.
    Je vais y jeter un oeil.

    Citation Envoyé par Mac LAK Voir le message
    Sinon, regarde du côté de cette page pour vérifier que tu n'aie pas une limitation via FD_SETSIZE... Pour ce que j'ai pu voir sur le reste (ex : limite en nombre de fichiers / handles ouverts), tous les chiffres par défaut explosent de très loin ta limite actuelle (ex : 512 fichiers simultanés au minimum pour les accès stdio), il est donc peu probable que ce soit en dehors de Winsock.
    FD_SETSIZE est défini à 64 par défaut, je ne l'ai pas changé et vérifié la valeur, qui me donne bien cette valeur.

    Citation Envoyé par Mac LAK Voir le message
    Du côté de ton code : tu es certain que ton while (true) { WithPool(); } ne crée pas une "infinité" de threads, donc de sockets, et donc sature Winsock ? Je n'ai pas testé ni compilé ton code, peut-être as-tu protégé derrière, mais ça n'en donne pas l'impression...
    Je lance des pools de NB_SOCKS_MAX threads. Chaque thread ouvre un socket, puis le referme au sein même du thread. J'attends que tous les threads de la pool ait fini son exécution via WaitForMultipleObjects() pour en lancer une nouvelle série, et ainsi de suite ... Donc à la fin de l'exécution d'une pool de thread, tous les sockets doivent être fermés.

  4. #4
    Inactif  
    Avatar de Mac LAK
    Profil pro
    Inscrit en
    Octobre 2004
    Messages
    3 893
    Détails du profil
    Informations personnelles :
    Âge : 49
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations forums :
    Inscription : Octobre 2004
    Messages : 3 893
    Points : 4 846
    Points
    4 846
    Par défaut
    Citation Envoyé par Trunks Voir le message
    Je lance des pools de NB_SOCKS_MAX threads. Chaque thread ouvre un socket, puis le referme au sein même du thread. J'attends que tous les threads de la pool ait fini son exécution via WaitForMultipleObjects() pour en lancer une nouvelle série, et ainsi de suite ... Donc à la fin de l'exécution d'une pool de thread, tous les sockets doivent être fermés.
    Essaie quand même en enlevant le while(true), et en faisant un ou deux appels "manuels" à WithPool... Au pire, ça te confirmera que ce n'est pas ça.

    Une autre piste potentielle d'erreur est le Sleep(100) que tu utilises au démarrage du thread : ce n'est pas bon, ni fiable. Il te faut soit utiliser un objet de synchro pour chaque thread, afin de savoir qu'il est initialisé, soit créer une copie dédiée à chaque thread au niveau de l'appelant (le thread pouvant se charger de libérer tout seul sa ressource au besoin).

    Dernier point : WSAStartup doit être la première fonction socket appelée par le PROCESSUS, mais tu n'as pas à l'appeler pour chaque thread. Pour rappel, cette fonction est toujours appelée en section critique par rapport à Winsock, donc l'appeler au sein de threads concurrents n'est pas spécialement une idée formidable. Essaie de déplacer son appel au niveau du main.

    Pas d'autres idées en tête, pour l'instant...
    Mac LAK.
    ___________________________________________________
    Ne prenez pas la vie trop au sérieux, de toutes façons, vous n'en sortirez pas vivant.

    Sources et composants Delphi sur mon site, L'antre du Lak.
    Pas de question technique par MP : posez-la dans un nouveau sujet, sur le forum adéquat.

    Rejoignez-nous sur : Serveur de fichiers [NAS] Le Tableau de bord projets Le groupe de travail ICMO

  5. #5
    Membre averti Avatar de Trunks
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Mai 2004
    Messages
    534
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 41
    Localisation : France, Val de Marne (Île de France)

    Informations professionnelles :
    Activité : Développeur informatique

    Informations forums :
    Inscription : Mai 2004
    Messages : 534
    Points : 412
    Points
    412
    Par défaut
    Citation Envoyé par Mac LAK Voir le message
    Essaie quand même en enlevant le while(true), et en faisant un ou deux appels "manuels" à WithPool... Au pire, ça te confirmera que ce n'est pas ça.
    Déjà testé et déjà eu des bloquages.

    Citation Envoyé par Mac LAK Voir le message
    Une autre piste potentielle d'erreur est le Sleep(100) que tu utilises au démarrage du thread : ce n'est pas bon, ni fiable. Il te faut soit utiliser un objet de synchro pour chaque thread, afin de savoir qu'il est initialisé, soit créer une copie dédiée à chaque thread au niveau de l'appelant (le thread pouvant se charger de libérer tout seul sa ressource au besoin).
    Comment je fais concrètement? Mon but est que le thread ait le temps de récupérer la valeur de lpParameter qui est une référence, donc faut que je fasse une copie avant qu'il soit remplacé par une autre valeur.

    Citation Envoyé par Mac LAK Voir le message
    Dernier point : WSAStartup doit être la première fonction socket appelée par le PROCESSUS, mais tu n'as pas à l'appeler pour chaque thread. Pour rappel, cette fonction est toujours appelée en section critique par rapport à Winsock, donc l'appeler au sein de threads concurrents n'est pas spécialement une idée formidable. Essaie de déplacer son appel au niveau du main.

    Pas d'autres idées en tête, pour l'instant...
    Merci pour l'info


    Note: J'ai modifié un peu le code en testant 20 serveurs en localhost sur des ports différents, et je n'ai eu aucun soucis, je peux lancer les 20 threads à la fois, essayer de me connecter sur des ports pas fermés (donc échec), ... les clients arrivent quasiment sans soucis à se connecter sur les serveurs dont les ports sont ouverts.

    Donc ce qui coince se situe apparemment quand je fais des adresses IP inexistants...

  6. #6
    Expert éminent sénior
    Avatar de Médinoc
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Septembre 2005
    Messages
    27 369
    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 369
    Points : 41 519
    Points
    41 519
    Par défaut
    Comment je fais concrètement? Mon but est que le thread ait le temps de récupérer la valeur de lpParameter qui est une référence, donc faut que je fasse une copie avant qu'il soit remplacé par une autre valeur.
    Soit tu passes par un Event, soit tu alloues avec malloc() et le thread s'occupe du free().

    Tu peux aussi te faire un tableau d'entiers et passer un pointeur au thread, du moment que le tableau possède une durée de vie supérieure à celle du thread...
    SVP, pas de questions techniques par MP. Surtout si je ne vous ai jamais parlé avant.

    "Aw, come on, who would be so stupid as to insert a cast to make an error go away without actually fixing the error?"
    Apparently everyone.
    -- Raymond Chen.
    Traduction obligatoire: "Oh, voyons, qui serait assez stupide pour mettre un cast pour faire disparaitre un message d'erreur sans vraiment corriger l'erreur?" - Apparemment, tout le monde. -- Raymond Chen.

  7. #7
    Inactif  
    Avatar de Mac LAK
    Profil pro
    Inscrit en
    Octobre 2004
    Messages
    3 893
    Détails du profil
    Informations personnelles :
    Âge : 49
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations forums :
    Inscription : Octobre 2004
    Messages : 3 893
    Points : 4 846
    Points
    4 846
    Par défaut
    Citation Envoyé par Trunks Voir le message
    Comment je fais concrètement? Mon but est que le thread ait le temps de récupérer la valeur de lpParameter qui est une référence, donc faut que je fasse une copie avant qu'il soit remplacé par une autre valeur.
    Comme te l'a dit Médinoc, soit un event, soit tu alloues dans le thread principal et tu libères dans le thread lancé.
    Si tu n'as pas trop familier avec ces mécanismes, je te conseille vivement d'allouer la structure pour le thread, et de le laisser libérer lui-même ses paramètres d'entrée. C'est, de loin, la méthode la plus rapide à implémenter même si elle pose des problèmes dans des cas complexes de multithreading (notamment quand tu dois assurer des performances maximales ET que chaque thread possède beaucoup de données initiales). Comme ce n'est pas ton cas, autant aller au plus simple.

    Citation Envoyé par Trunks Voir le message
    Donc ce qui coince se situe apparemment quand je fais des adresses IP inexistants...
    Normalement, tenter d'accéder à une IP inexistante va déclencher un timeout, qui est classiquement de 30 secondes à peu près... Es-tu sûr de ne pas être dans ce cas de figure ?
    Mac LAK.
    ___________________________________________________
    Ne prenez pas la vie trop au sérieux, de toutes façons, vous n'en sortirez pas vivant.

    Sources et composants Delphi sur mon site, L'antre du Lak.
    Pas de question technique par MP : posez-la dans un nouveau sujet, sur le forum adéquat.

    Rejoignez-nous sur : Serveur de fichiers [NAS] Le Tableau de bord projets Le groupe de travail ICMO

  8. #8
    Membre averti Avatar de Trunks
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Mai 2004
    Messages
    534
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 41
    Localisation : France, Val de Marne (Île de France)

    Informations professionnelles :
    Activité : Développeur informatique

    Informations forums :
    Inscription : Mai 2004
    Messages : 534
    Points : 412
    Points
    412
    Par défaut
    J'ai suivi tes conseils:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    #include <winsock2.h>
    #include <Thread/Lock.h>
    #include <stdio.h>
    
    #define NB_SOCKS_MAX 10
    
    #pragma comment(lib, "Ws2_32.lib")
    
    void CreatePoolThreads(int iIndexMin, int iIndexMax);
    int ConnectToServer(char * lpszServer, int iPort);
    DWORD WINAPI StartThread(LPVOID lpParam);
    
    int main()
    {
        WSADATA wsaData;
    
        if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0)
    	{
            fprintf(stderr, "La fonction WSAStartup a echoue.\n");
    		return EXIT_FAILURE;
    	}
    
    	while (true)
    	{
    		CreatePoolThreads(39, 48);
    	}
    
    	WSACleanup();
    
    	return EXIT_SUCCESS;
    }
    
    void CreatePoolThreads(int iIndexMin, int iIndexMax)
    {
    	HANDLE achHThread[NB_SOCKS_MAX] = { NULL };
    	int iIndex = iIndexMin;
    
    	while (iIndex <= iIndexMax)
    	{
    		int iPoolCount = (iIndex + NB_SOCKS_MAX - 1 <= iIndexMax) ? NB_SOCKS_MAX : iIndexMax - iIndex + 1;
    		int tab[NB_SOCKS_MAX];
    		int i;
    
    		for (i = 0; i < iPoolCount; ++i)
    		{
    			tab[i] = iIndex;
    
    			achHThread[i] = CreateThread(NULL, 0, static_cast<LPTHREAD_START_ROUTINE>(StartThread), &tab[i], 0, NULL);
    
    			++iIndex;
    		}
    		
    		WaitForMultipleObjects(iPoolCount, achHThread, TRUE, INFINITE);
    
    		for (i = 0; i < NB_SOCKS_MAX; ++i)
    		{
    			if (achHThread[i] && (achHThread[i] != INVALID_HANDLE_VALUE))
    			{
    				CloseHandle(achHThread[i]);
    				achHThread[i] = NULL;
    			}
    		}
    	}
    }
    
    int ConnectToServer(char * lpszServer, int iPort)
    {
    	{
    		Thread::CLock lock("print");
    		fprintf(stdout, "Connexion au serveur %s sur le port %d.\n", lpszServer, iPort);
    	}
    
        SOCKET s;
        
        s = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
        
    	if (s == INVALID_SOCKET)
    	{
            fprintf(stderr, "La fonction socket a echoue (%s).\n", lpszServer);
    		return -1;
    	}
        else
        {
            SOCKADDR_IN server;
    		ULONG fionbio;
            
            server.sin_family       = AF_INET;
            server.sin_addr.s_addr  = inet_addr(lpszServer);
            server.sin_port         = htons(iPort);
            memset(&server.sin_zero, '\0', sizeof(server.sin_zero));
    
    		// Mode non-bloquant
    		fionbio = TRUE;
    		ioctlsocket(s, FIONBIO, &fionbio);
    
    		if ((connect(s, (SOCKADDR *)&server, sizeof(server))) == SOCKET_ERROR)
    		{
    			fd_set writefds;
    			struct timeval tv;
    					
    			tv.tv_sec = 3;
    			tv.tv_usec = 0; 
    
    			FD_ZERO(&writefds);
    			FD_SET(s, &writefds);
    			select(0, NULL, &writefds, NULL, &tv);
    
    			// Retour au mode bloquant
    			fionbio = FALSE;
    			ioctlsocket(s, FIONBIO, &fionbio);
    
    			if (FD_ISSET(s, &writefds))
    			{
    				{
    					Thread::CLock lock("print");
    					fprintf(stdout, "%s connecte.\n", lpszServer);
    				}
    
    				char buffer[100];
    				int n;
            
    				n = recv(s, buffer, sizeof(buffer) - 1, 0);
            
    				if (n != SOCKET_ERROR)
    				{
    					buffer[n] = '\0';
    
    					{
    						Thread::CLock lock("print");
    						fprintf(stdout, "%s", buffer);
    					}
                
    					{
    						Thread::CLock lock("send");
    						send(s, "Au revoir\n", (int)strlen("Au revoir\n"), 0);
    					}
    				}
            
    				shutdown(s, SD_SEND);
    			}
    			else
    			{
    				{
    					Thread::CLock lock("print");
    					fprintf(stderr, "%s non connecte avec le port %d.\n", lpszServer, iPort);
    				}
    			}
    		}
    
            closesocket(s);
        }
    
    	return 0;
    }
    
    DWORD WINAPI StartThread(LPVOID lpParam)
    {
    	char achServer[15];
    	int iCr;
    
    	int * lpiNum = reinterpret_cast<int *>(lpParam);
    	int iNum = *lpiNum;
    
    	sprintf(achServer, "172.16.20.%d", iNum);
    
    	iCr = ConnectToServer(achServer, 3650);
    
    	return 0;
    }
    Cependant, j'ai toujours le même soucis, ça marche quand j'ai un seul thread à la fin, et plus que j'ai de threads, moins que ça marche. Le client a de plus en plus de mal à se connecter à serveur mis en place sur mon PC (172.16.20.48).

    Y a-t-il un problème avec mon code?

  9. #9
    Inactif  
    Avatar de Mac LAK
    Profil pro
    Inscrit en
    Octobre 2004
    Messages
    3 893
    Détails du profil
    Informations personnelles :
    Âge : 49
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations forums :
    Inscription : Octobre 2004
    Messages : 3 893
    Points : 4 846
    Points
    4 846
    Par défaut
    Citation Envoyé par Trunks Voir le message
    Y a-t-il un problème avec mon code?
    Pas au coup d'oeil, en tout cas, du moins je ne vois plus rien de particulier...

    Par curiosité, as-tu essayé avec le serveur sur une autre machine physique, afin de vérifier si ce ne serait pas un problème de boucle locale ?
    Mac LAK.
    ___________________________________________________
    Ne prenez pas la vie trop au sérieux, de toutes façons, vous n'en sortirez pas vivant.

    Sources et composants Delphi sur mon site, L'antre du Lak.
    Pas de question technique par MP : posez-la dans un nouveau sujet, sur le forum adéquat.

    Rejoignez-nous sur : Serveur de fichiers [NAS] Le Tableau de bord projets Le groupe de travail ICMO

  10. #10
    Membre averti Avatar de Trunks
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Mai 2004
    Messages
    534
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 41
    Localisation : France, Val de Marne (Île de France)

    Informations professionnelles :
    Activité : Développeur informatique

    Informations forums :
    Inscription : Mai 2004
    Messages : 534
    Points : 412
    Points
    412
    Par défaut
    Citation Envoyé par Mac LAK Voir le message
    Pas au coup d'oeil, en tout cas, du moins je ne vois plus rien de particulier...

    Par curiosité, as-tu essayé avec le serveur sur une autre machine physique, afin de vérifier si ce ne serait pas un problème de boucle locale ?
    Oui, j'ai testé, d'ailleurs c'est le cas en réel, là je me suis fait un exemple local où je n'ai gardé que la partie serveur et j'ai viré tout le reste.

    Je viens de remarquer un truc interressant. J'ai repassé la connexion du socket en mode bloquant. Le timeout est de 20s. Quand je lance 20 threads parallèles sur des IPs inconnus, alors au bout de 20s, j'ai 10 ou 11 threads qui s'arrêtent, et encore 20s plus tard les 9 ou 10 restants. Donc ça confirme qu'il y a un nombre limité de connexion de sockets simultannés, les autres sont en attente que ça se libère (sémaphore?). Bref, ça conforte l'idée de faire des pools de 10 threads.

    En mode bloquant, ça marche parfaitement, j'ai 9 threads qui pointent vers un IP inconnu ou un IP n'hébergeant pas de serveur sur le port sélectionné, et le 10e thread sur un serveur valide avec le bon port. Par contre, une fois que je passe en mode non bloquant pour gérer le timeout de connexion (via ioctlsocket() et select()), alors ça ne marche plus correctement

    Donc c'est de ce côté qu'il faudrait regarder, je ne sais pas exactement comment ça fonctionne derrière ...

    Ou bien peut-être que malgré le timeout du select(), la ressource du socket ne soit pas tout de suite libéré, du coup à un moment il n'y a plus de socket disponible, ce qui expliquerait pourquoi ça marche à la 1e boucle mais coince ensuite. Cependant, il y a closesocket() qui devrait libérer la ressource, alors c'est curieux

  11. #11
    Membre averti Avatar de Jenna
    Inscrit en
    Décembre 2009
    Messages
    272
    Détails du profil
    Informations personnelles :
    Âge : 39

    Informations forums :
    Inscription : Décembre 2009
    Messages : 272
    Points : 339
    Points
    339
    Par défaut
    Est ce qu'il est possible d'imaginer que le select() se décoince une fois pour plusieurs événements simultanés et que tu n'en traites qu'un seul ? Ce qui te donnerais l'impression que tu ne les recois pas tous.
    La valeur n'attend pas le nombre des années

  12. #12
    Membre averti Avatar de Trunks
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Mai 2004
    Messages
    534
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 41
    Localisation : France, Val de Marne (Île de France)

    Informations professionnelles :
    Activité : Développeur informatique

    Informations forums :
    Inscription : Mai 2004
    Messages : 534
    Points : 412
    Points
    412
    Par défaut
    J'ai trouvé ce lien qui explique qu'en effet, le nombre de connexion TCP/IP est limité à 10 par seconde.

  13. #13
    Membre averti Avatar de Trunks
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Mai 2004
    Messages
    534
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 41
    Localisation : France, Val de Marne (Île de France)

    Informations professionnelles :
    Activité : Développeur informatique

    Informations forums :
    Inscription : Mai 2004
    Messages : 534
    Points : 412
    Points
    412
    Par défaut
    Citation Envoyé par Jenna Voir le message
    Est ce qu'il est possible d'imaginer que le select() se décoince une fois pour plusieurs événements simultanés et que tu n'en traites qu'un seul ? Ce qui te donnerais l'impression que tu ne les recois pas tous.
    Dans mon cas, j'ai un select() par thread. Ce dernier vérifie que le socket ouvert dans le thread est disponible en écriture d'après ce que j'ai compris. Mais je ne comprends pas exactement le mécanisme.

  14. #14
    Membre averti Avatar de Trunks
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Mai 2004
    Messages
    534
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 41
    Localisation : France, Val de Marne (Île de France)

    Informations professionnelles :
    Activité : Développeur informatique

    Informations forums :
    Inscription : Mai 2004
    Messages : 534
    Points : 412
    Points
    412
    Par défaut
    En mode non-bloquant, j'ai mis 25s en timeout, soit un peu plus que le timeout en mode bloquant, et là, je n'ai aucun problème, le thread arrive à se connecter au serveur sans problème instantanement. Par contre, quand j'abaisse le timeout, alors j'observe des décalages pour la connexion au serveur (souvent instantané à part la 1e fois), et parfois le timeout est atteint (ex: quand timeout = 5s) Quelque chose doit parasiter ...

  15. #15
    Membre averti Avatar de Trunks
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Mai 2004
    Messages
    534
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 41
    Localisation : France, Val de Marne (Île de France)

    Informations professionnelles :
    Activité : Développeur informatique

    Informations forums :
    Inscription : Mai 2004
    Messages : 534
    Points : 412
    Points
    412
    Par défaut
    Une piste dans le MSDN du connect():

    For connection-oriented, nonblocking sockets, it is often not possible to complete the connection immediately. In such a case, this function returns the error WSAEWOULDBLOCK. However, the operation proceeds. When the success or failure outcome becomes known, it may be reported in one of several ways depending on how the client registers for notification. If the client uses the select function, success is reported in the writefds set and failure is reported in the exceptfds set. If the client uses the functions WSAAsyncSelect or WSAEventSelect, the notification is announced with FD_CONNECT and the error code associated with the FD_CONNECT indicates either success or a specific reason for failure
    Comment interpréter ça?

  16. #16
    Inactif  
    Avatar de Mac LAK
    Profil pro
    Inscrit en
    Octobre 2004
    Messages
    3 893
    Détails du profil
    Informations personnelles :
    Âge : 49
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations forums :
    Inscription : Octobre 2004
    Messages : 3 893
    Points : 4 846
    Points
    4 846
    Par défaut
    En étant lapidaire, comme "ne pas utiliser connect mais les autres fonctions ayant un effet similaire"... L'idéal semblant être d'utiliser WSAEventSelect, d'ailleurs, qui semble la plus "propre".

    En gros : le statut de connexion peut ne pas être immédiatement définissable, et renvoyer une pseudo-erreur WSAEWOULDBLOCK (à traiter comme une erreur EINVAL, apparemment, c'est à dire "merci de réessayer plus tard").
    Si (et seulement si) c'est ce code d'erreur qui remonte, alors le statut réel de la connexion (OK ou KO) sera remonté par les méthodes citée à la fin du paragraphe. J'ai l'impression, d'après les explications, qu'utiliser directement WSAEventSelect permettrait de ne pas avoir directement le problème et/ou d'avoir la solution juste après, le problème semblant venir de l'utilisation exclusive de select sans utiliser conjointement les autres fonctions Winsock.
    Mac LAK.
    ___________________________________________________
    Ne prenez pas la vie trop au sérieux, de toutes façons, vous n'en sortirez pas vivant.

    Sources et composants Delphi sur mon site, L'antre du Lak.
    Pas de question technique par MP : posez-la dans un nouveau sujet, sur le forum adéquat.

    Rejoignez-nous sur : Serveur de fichiers [NAS] Le Tableau de bord projets Le groupe de travail ICMO

  17. #17
    Membre averti Avatar de Trunks
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Mai 2004
    Messages
    534
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 41
    Localisation : France, Val de Marne (Île de France)

    Informations professionnelles :
    Activité : Développeur informatique

    Informations forums :
    Inscription : Mai 2004
    Messages : 534
    Points : 412
    Points
    412
    Par défaut
    Après plusieurs tentatives, je n'ai toujours pas trouvé. Tant pis, je vais faire sans. Merci pour votre aide.

Discussions similaires

  1. Réponses: 9
    Dernier message: 04/04/2011, 10h20
  2. fermeture socket dans un thread
    Par adrien1 dans le forum Débuter
    Réponses: 0
    Dernier message: 20/04/2010, 14h45
  3. Probleme de socket dans un thread
    Par nikus dans le forum Threads & Processus
    Réponses: 4
    Dernier message: 25/06/2008, 03h27
  4. IOException sur socket dans un package
    Par gint dans le forum Entrée/Sortie
    Réponses: 4
    Dernier message: 29/03/2006, 19h45
  5. Sockets et threads...
    Par Kiff dans le forum Réseau/Web
    Réponses: 6
    Dernier message: 27/07/2004, 17h35

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