[C] Problème de sockets

Problème de sockets [C] - C - Programmation

Marsh Posté le 18-01-2006 à 14:54:55    

Bonjour à tous!
 
Alors voilà, j'ai un serveur qui tourne sur une machine Linux (codé avec des pthreads). Je voudrais tester s'il peut supporter environ 1000 clients. Les clients envoient deux types de messages au serveur:
 
"login;machine;ip;l" ou "ip"
 
Mon client tourne sous windows mais aussi sous linux. Le problème est le suivant : vu que je n'ai pas 1000 pc pour simuler les clients, je lance le client plusieurs fois sur la même machine (Win XP SP2, Win 2000 Server ou Debian).
 
Le client se comporte comme ceci:
 

Code :
  1. envoi "login;machine;ip;l"
  2. tant que vrai faire
  3.   envoi ip
  4.   attendre 5 secondes
  5. ftant


 
Au niveau du serveur, tout semble bien se passer mais le "semble" est trop approximatif. Je voudrais en etre sûr mais j'ai des clients qui plantent de temps en temps au moment du connect. Je lance une centaine de client sur mes postes windows 2000 et 50 sur les windows XP SP2. J'en ai une quarantaine qui tournent sur linux et qui ne semblent pas planter.
 
En fait, j'ai l'impression que c'est windows qui a du mal à gérer le nombre important de connexions, d'autant plus qu'un netstat me sort pas moins de 800 connexions dans l'état TIME_WAIT.
 
Puis je conclure que mon problème vient de windows? Avez vous des idées pour que je puisse m'assurer que mon serveur tourne bien?
 
Merci d'avance pour votre aide. :-)
 
[EDIT] lorsque le connect échoue et renvoie une valeur négative, j'affiche la valeur de errno qui est 0, je trouve ça assez louche quand même...


Message édité par _p1c0_ le 18-01-2006 à 14:57:51
Reply

Marsh Posté le 18-01-2006 à 14:54:55   

Reply

Marsh Posté le 18-01-2006 à 15:55:05    

En effet depuis le XP / SP2 windows a mis des limitations au niveau du nombre de connections. En fait il y a surtout une limite au nombre de connections possible dans un temps donne. Je crois que passe cette limite, on se retrouve avec une socket error 10048 (a verifier).
J'avais vu un patch qui permettait de restaurer le tcpip.sys du SP1 sur le SP2 et donc faisait sauter cette limite.
Le fait d'avoir les connections en FIN_WAIT c'est normal en cas de "garcefull close" (no linger), en fait ces connections peuvent rester jusqu'a 240 secondes avant d'etre reutilisee (valeur par default).
Tu peux dans les registre changer cette valeur, elle correspond a la value "TcpTimedWaitDelay", et la valeur mini est 30 .. donc 30 secondes. Il faut rebooter apres (ca doit etre dans HKEY_LOCAL_MACHINE\Service\currentControlSet\Tcpip\parameters), si la value n'y est pas c'est le defaut 240 qui est applique. A verifier egalement la valeur "TcpNumConnection" ..
 
J'ai eu pas mal de probleme de ce genre avec XP mais pas avecwindows 2000 server (apres avoir change TimedWaitDelay)..

Reply

Marsh Posté le 18-01-2006 à 16:47:29    

J'ai bien ajouté la clé TcpTimedWaitDelay mais sans grand succès... Le nombre de TIME_WAIT dans netstat reste important mais il a diminué. Par contre, j'ai toujours ces plantages réguliers.
 
Je vais essayer du côté de TcpNumConnection puisque ca semble moins planter sous windows 2000 server, il se peut que ca soit ça qui pose problème.
 
Si d'autres personnes ont des idées, je suis preneur! :p
 
EDIT: il arrive aussi que d'un coup, tous les clients lancés sur un poste plantent mais que ceux des autres postes continue leurs envois... je n'imagine pas bien que le serveur puisse être en cause dans ce genre de cas...


Message édité par _p1c0_ le 18-01-2006 à 16:49:25
Reply

Marsh Posté le 18-01-2006 à 16:57:54    

Je pense que le probleme viens du nombre de socket sous windows en general. Est ce que tu recupere le code d'erreur dans le cas de plantage ??
Esssayes aussi l'option SO_REUSEADDR pour les sockets clients, car d'apres les RFC on ne peut pas reutiliser la meme socket pair (ie : IP + port) durant le timedwaitdelay.
Bien sur, verifie aussi que les sockets sont bien "fermees" .....
 
J'ai teste ce genre de petit client server avec un client Windows 2000 server sans problemes.. par contre en XP/SP2, il ne tourne que 2 minutes et apres impossible de creer les sockets.

Reply

Marsh Posté le 18-01-2006 à 17:04:08    

fais bien attention de bien refermer tes sockets clients au niveau du serveur. Si tu peux les fermer proprement au niveau du client, c'est aussi bien.
Ce n'est pas un problème de SO_REUSEADDR car cette option sert pour bind.

Reply

Marsh Posté le 18-01-2006 à 17:10:00    

Taz a écrit :

fais bien attention de bien refermer tes sockets clients au niveau du serveur. Si tu peux les fermer proprement au niveau du client, c'est aussi bien.
Ce n'est pas un problème de SO_REUSEADDR car cette option sert pour bind.


 
Dans mon client je fais un simple close(socket), c'est pas suffisant ? J'ai lu que sous linux, il fallait faire un unlink sur  la socket, est ce vrai?
 
Dans mon serveur, je fais un close(socket) aussi. Par contre, il faut peut être un unlink dessus puisque le serveur est sous linux...
 
Merci pour votre aide :)

Reply

Marsh Posté le 18-01-2006 à 17:15:50    

Le connect fait un bind implicitement de toute facon ... Enfin sur XP/SP2, j'ai retrouve la source de la limitation :
 
http://www.speedguide.net/read_articles.php?id=1497

Reply

Marsh Posté le 18-01-2006 à 17:19:32    

unlink c'est pour les fichiers. fais bien tes close, fais bien attention de bien closer meme en cas d'erreur. ce que tu peux faire par exemple, c'est faire un strace -f et tracer les descripteurs de fichiers, voir si tu n'en perds pas.
 
connect bind certes, mais il va te filer un nouveau port, s'il en trouve pas, c'est le noyau qui va faire du nettoyage. si y en a vraiment pas, il te donnera -1.

Reply

Marsh Posté le 18-01-2006 à 17:30:02    

Oui il doit faire le nettoyage mais sur windows il attend le fameux TcpTimedWait delay pour nettoyer les connections en TIME_WAIT. Le truc plus economique aussi c'est de faire un half-close cote client, c'est a dire de faire un shutdown(SO_SND) apres avoir envoye les donnees (si c'est possible, suivant le scenario). La socket peut encore recevoir mais elle ne peut plus envoyer. Les socket seront en FIN_WAIT et plus en TIME_WAIT.
 
Au sujet de windows, le lien (bas de page) explique aussi pas mal de chose sur la protection contre les SYN attacks et le time wait.
 
http://www.microsoft.com/technet/c [...] g1204.mspx

Reply

Marsh Posté le 18-01-2006 à 17:44:40    

Taz a écrit :

fais bien tes close, fais bien attention de bien closer meme en cas d'erreur.


 
J'ai vérifié, toujours un close dans le client et dans le serveur même encas d'erreur.
 

Taz a écrit :

ce que tu peux faire par exemple, c'est faire un strace -f et tracer les descripteurs de fichiers, voir si tu n'en perds pas.


 
Je connaissais pas. J'ai esayé avec mon client linux, si j'ai bien compris le principe, aucun descripteur n'est perdu. Sur les clients windows, j'ai pas encore cherché s'il y avait un équivalent (peut être avec un cygwin mais j'en ai pas sous la main là).
 
Du côté du serveur,ca va être dur de tester puisque c'est une système Linux From Scratch donc pas de strace. Je peux toujours essayer d'en copier un comme ca dessus mais je risque d'avoir des problèmes de libs... :(

Reply

Marsh Posté le 18-01-2006 à 17:44:40   

Reply

Marsh Posté le 18-01-2006 à 17:50:57    

francky06l a écrit :

Oui il doit faire le nettoyage mais sur windows il attend le fameux TcpTimedWait delay pour nettoyer les connections en TIME_WAIT. Le truc plus economique aussi c'est de faire un half-close cote client, c'est a dire de faire un shutdown(SO_SND) apres avoir envoye les donnees (si c'est possible, suivant le scenario). La socket peut encore recevoir mais elle ne peut plus envoyer. Les socket seront en FIN_WAIT et plus en TIME_WAIT.


 
Enfin moi mon but là, c'est pas de modifier le client pour pouvoir en faire tourner 1000 sur un poste windows puisqu'il y en aura qu'un par poste. Ce que je veux, c'est voir jusqu'à combien de clients supporte mon serveur dans le but de placer ce serveur dans un réseau de 800 postes par exemple. Je veux juste faire des tests en internes mais je n'ai pas assez de pc disponibles...

Reply

Marsh Posté le 18-01-2006 à 17:57:58    

Tu peux creer un client multithread, si le code est light to lui colles 256 threads, ca te permettrait avec 4 ordis de tester deja pas mal de connections simultanees. Il est vrai que meme en multithread, ca va quand meme se mettre en queue au niveau bas TCP mais ca donne une idee quand meme.
 

Reply

Marsh Posté le 18-01-2006 à 18:08:32    

francky06l a écrit :

Tu peux creer un client multithread, si le code est light to lui colles 256 threads, ca te permettrait avec 4 ordis de tester deja pas mal de connections simultanees. Il est vrai que meme en multithread, ca va quand meme se mettre en queue au niveau bas TCP mais ca donne une idee quand meme.


 
Ben là ce que j'ai fait, c'est un batch qui lance 50 invites de commandes qui exécutent toutes mon client avec des paramètres différents... C'est pas très propre mais ca teste ce que je veux.

Reply

Marsh Posté le 19-01-2006 à 08:45:46    

Bien, j'ai laissé tourner 300 clients toute la nuit.A mon arrivée ce matin, il n'y en a plus que 39 qui tournent et ce sont mes clients linux... Tous les clients windows sont tombés (aussi bien sur XP que sur 2000 Server). Le serveur semble toujours faire son boulot puisque les 39 clients linux sont toujours là.
 
Il y a un truc qui m'intrigue quand même sur windows. Quand le connect renvoie une valeur négative, j'affiche la valeur d'errno. Celle-ci est à 0 or :
 

Code :
  1. sh-2.05b$ perror 0
  2. Error code   0 :  Success
  3. sh-2.05b$


 
Errno ca marche bien sous windows aussi??? Qu'est ce qui peut se passer???
 
Merci pour votre aide.

Reply

Marsh Posté le 19-01-2006 à 09:51:33    

Pour connaitre l'erreur socket sous windows, il faut utiliser GetLastError() ou WSAGetLastError(), les sockets error ne sont pas gerees avec errno. Tu peux trouver les codes d'erreur ( code > 10000) dans winsock.h ou winsock2.h
 
Dans mes sources j'ai mis ce genre de truc :
 
#ifdef _UNIX
DWORD GetLastError()
{
     return(errno);
}
 
et j'utilise GetLastError() partout.

Reply

Marsh Posté le 19-01-2006 à 10:16:43    

Ok, donc je viens de remplacer mon errnor par GetLastError() qui me retourne 10060 qui correspond à un TimeOut. Maintenant, j'ai du mal à comprendre que cela se produit quasiment qu'avec les postes windows. Là sous linux j'en ai lancé 200 qui se connectent toutes les 2 secondes (au lieu de 5) et ils tombent beaucoup moins que les clients windows. Le TimeOut n'est pas le même sous windows et sous linux?
 
EDIT : bon ce coup là, je pense que ca vient quand même de mon serveur sous linux qui est dépassé même si j'en avais pas l'impression... Si je créé plusieurs threads, chacun écoutant sur des ports différents, et que dans mon client, je choisis un port au hasard dans l'ensemble de ports utilisés, ca devrait réduire un peu ce genre de problème non? (comme c'est là, j'ai un thread en écoute sur le port 1025 et je créé un thread à chaque nouvelle connexion...)


Message édité par _p1c0_ le 19-01-2006 à 10:30:39
Reply

Marsh Posté le 19-01-2006 à 10:28:18    

francky06l a écrit :

Oui il doit faire le nettoyage mais sur windows il attend le fameux TcpTimedWait delay pour nettoyer les connections en TIME_WAIT. Le truc plus economique aussi c'est de faire un half-close cote client, c'est a dire de faire un shutdown(SO_SND) apres avoir envoye les donnees (si c'est possible, suivant le scenario). La socket peut encore recevoir mais elle ne peut plus envoyer. Les socket seront en FIN_WAIT et plus en TIME_WAIT.

ce n'est pas une spécifité windows

Reply

Marsh Posté le 19-01-2006 à 10:47:40    

Taz a écrit :

ce n'est pas une spécifité windows


C'est vrai :-)
 
Pour ce qui est du timeout connect sous windows, je pense que c'est 3 secondes mais il y a les retry et l'interval entre les retry ..Bref tout ca est controlle par les registry. J'ai la liste au format word, PM moi je te  l'envoi.
 
Sinon, pour le server, demarrer un thread a chaque fois cest quand meme couteux, cote serveur j'ai gere ca avec une queue :
 
- Une thread qui fait juste l'accept et push dans la queue la socket
- N threads (N parametrable) qui pop la queue et process la connection
 
La synchronisation est faire avec une semaphore (win32) et avec une pthread_mutex sur UNIX.
La queue est une queue STL qui maintien la synchro avec un Event (Win32) ou un mutex sur UNIX.
 
Quand une connection arrive, la "requester" thread empile la connection et signale le Semaphore.
Chaque "process" thread attend le signalement du semaphore et "pop" la queue pour recuperer la connection.
Au demarrage les N Thread sont demarrees et sont en wait, sans consommer beaucoup de resources .. Ca evite le demarrage de thread a chaque connection.
La connection cote client se fait vite car la requester n'a potientiellement rien a faire et boucle sur l'accept, bien sur plus la queue grandit plus le temps de process cote client augmente, il faut faire du tunning..

Reply

Marsh Posté le 19-01-2006 à 11:25:25    

Ok, ce genre de structure semble pas mal, mais est ce que je peux être sur que ca va résoudre mon problème?
 
Juste le fait de faire un push dans la file au lieu de faire un create_thread va me permettre d'augmenter beaucoup la vitesse de traitement des requêtes?  
 
Par exemple, un serveur web, c'est codé avec ce genre de technique?
 
Merci pour votre aide :)

Reply

Marsh Posté le 19-01-2006 à 11:34:47    

Ce genre de technique est utilise sur IIS (web Win32) et quelques autres web. Quelques autres font aussi du fork ..etc ...
 
La vitesse de "process" des requetes ne changera pas, mais ton server evitera de demarrer des threads pour chaque connection. Le demarrage/stop d'une thread a un cout que tu evitera pour chaque connection, en ce sens tu gagneras du temps.
Attention ce modele est valide pour un mode purement "Transactionnel" (c a d : client se connecte a chaque fois pour faire quelque chose), oppose a un mode genre "passif" ou le client fait un seul connect au debut et maintient la connection tout le temps. Pour ce dernier, c'est faisable egalement en "derivant" le premier modele.
Un truc economique aussi dans la communication socket, c'est de faire en mode asynchrone.

Reply

Marsh Posté le 19-01-2006 à 11:44:03    

non merci. Sous Linux, ce genre de truc se configure par /proc/sys/net

Reply

Marsh Posté le 19-01-2006 à 11:46:49    

Donc si je fais comme ça, les accept se feront plus vite. Le traitement des requêtes n'ira pas plus vite c'et sûr mais moi je veux éviter les TimeOut au niveau du client. Je pense que je vais coder ça. Enfin mon code est en C donc avec une queue de la STL ca va pas faire beau tout ça!
 
Si d'autres personnes ont des retours d'expérience à ce sujet, je suis preneur :)

Reply

Marsh Posté le 19-01-2006 à 11:50:55    

Taz a écrit :

non merci. Sous Linux, ce genre de truc se configure par /proc/sys/net


Oui, mais si tu veux le faire par programmation quand tu n'est pas priopritaire et admin de la machine sur laquelle ton programme tourne. Bref au niveau de la socket elle meme, quand tu fait du code pour tous os. Sous Solaris / AIX par exemple comme sous Win32 ...
No problem

Reply

Marsh Posté le 26-01-2006 à 18:16:48    

Bonjour à tous,
 
je déterre un peu le topic mais c'est tout simplement parce que je viens enfin de coder le serveur avec une file de connections... Seul problème : ca ne suffit pas!
 
J'arrive à atteindre les 1000 clients qui se connectent toutes les 5 secondes mais ca ne dure pas longtemps! J'en ai régulièrement qui n'arrive pas à se connecter au serveur...
 
Vu que je bosse aussi avec DansGuardian an ce moment, j'ai voulu voir comment c'était géré puisqu'avec une dizaine de processus à peine, il supporte un nombre important de requêtes. J'ai pu voir que c'était codé avec des forks et que les descripteurs de socket sont échangés via des pipe.  
 
Comme je l'ai déjà dit, l'utilisation des forks me pose problème car je dois gérer une table de hachage. Je pourrais utiliser de la mémoire partagée mais je connais pas trop et je sais pas si ca pourrait résoudre mon problème.. Vous en pensez quoi?
 
Sinon, j'ai vu que dans dansguardian, l'appel à listen est fait avec le descripteur de la socket et un entier (256). J'ai été voir à quoi correspond cet entier dans le man (nombre maximum de connections en queue) et là j'ai vu que moi, l'appel est fait avec 1 à la place de 256. J'ai donc changé ça mais les résultats sont les mêmes, voire même pire !
 
Avez vous des idées sur ce qui pourrait poser problème?
 
Merci d'avance.

Reply

Marsh Posté le 27-01-2006 à 16:29:26    

Bonjour _p1c0_,
 
Je pense que ton problème vient du nombre de ports disponibles (automatiques) utilisés par le connect() des clients.
 
Dans la registry, il faut rajouter/modifier la valeur DWORD "HKLM\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters\MaxUserPort" à 65535 (décimal, ou moins si tu veux => 32768 par exemple) pour avoir 65535-1024=64511 port disponibles.
 
Par défaut (absente), la valeur est de 4096, donc les connect() peuvent utiliser 4096-1024=3072 (moins les ports bindés, et ceux déjà utilisés par les autres applis).
 
Bref, cette plage de port peut vite être saturée en cas de stress (1000 clients par exemple) et de sockets en TIME_WAIT (qui restent inutilisables pendant 240s par défaut, 30s en mettant TcpTimedWaitDelay à 30 au même endroit).
 
Ca devrait peut être résoudre ton problème ...

Reply

Marsh Posté le    

Reply

Sujets relatifs:

Leave a Replay

Make sure you enter the(*)required information where indicate.HTML code is not allowed