TcpListener est de files d'attente des connexions plus vite que je peux les effacer
Comme je le comprends, TcpListener
aura la file d'attente de connexions une fois que vous appelez Start()
. Chaque fois que vous appelez AcceptTcpClient
(ou BeginAcceptTcpClient
), il va retirer un élément de la file d'attente.
Si nous charger de tester notre TcpListener
application par l'envoi de 1 000 connexions à la fois, la file d'attente s'appuie beaucoup plus vite que nous pouvons l'effacer, conduisant éventuellement à des délais d'attente de la part du client, car il n'a pas obtenu une réponse parce que sa connexion est toujours dans la file d'attente. Toutefois, le serveur ne semble pas être sous beaucoup de pression, notre application n'est pas en consommer beaucoup de temps PROCESSEUR et les autres ressources surveillées sur la machine ne sont pas casser une sueur. Il se sent comme nous ne sommes pas en cours d'exécution assez efficace de la droite maintenant.
Nous appelons BeginAcceptTcpListener
, puis immédiatement à la remise à un ThreadPool
thread réellement faire le travail, puis de l'appel d' BeginAcceptTcpClient
de nouveau. Le travail ne semble pas mettre de pression sur la machine, il est fondamentalement juste 3 seconde de sommeil suivie par une recherche dans le dictionnaire et puis un 100 octets à écrire à l' TcpClient
's de flux.
Voici la TcpListener
code que nous avons utilisé:
//Thread signal.
private static ManualResetEvent tcpClientConnected = new ManualResetEvent(false);
public void DoBeginAcceptTcpClient(TcpListener listener)
{
//Set the event to nonsignaled state.
tcpClientConnected.Reset();
listener.BeginAcceptTcpClient(
new AsyncCallback(DoAcceptTcpClientCallback),
listener);
//Wait for signal
tcpClientConnected.WaitOne();
}
public void DoAcceptTcpClientCallback(IAsyncResult ar)
{
//Get the listener that handles the client request, and the TcpClient
TcpListener listener = (TcpListener)ar.AsyncState;
TcpClient client = listener.EndAcceptTcpClient(ar);
if (inProduction)
ThreadPool.QueueUserWorkItem(state => HandleTcpRequest(client, serverCertificate)); //With SSL
else
ThreadPool.QueueUserWorkItem(state => HandleTcpRequest(client)); //Without SSL
//Signal the calling thread to continue.
tcpClientConnected.Set();
}
public void Start()
{
currentHandledRequests = 0;
tcpListener = new TcpListener(IPAddress.Any, 10000);
try
{
tcpListener.Start();
while (true)
DoBeginAcceptTcpClient(tcpListener);
}
catch (SocketException)
{
//The TcpListener is shutting down, exit gracefully
CheckBuffer();
return;
}
}
Je suis en supposant que la réponse va être liés à l'utilisation de Sockets
au lieu de TcpListener
, ou au moins à l'aide TcpListener.AcceptSocket
, mais je me demandais comment nous allions faire cela?
Une idée que nous avions était d'appel AcceptTcpClient
et immédiatement Enqueue
la TcpClient
dans l'une des multiples Queue<TcpClient>
objets. De cette façon, nous pourrions sondage de ces files d'attente sur des threads séparés (une file d'attente par thread), sans que des moniteurs qui pourraient bloquer le thread en attendant que d'autres Dequeue
opérations. Chaque file d'attente thread pourrait ensuite utiliser ThreadPool.QueueUserWorkItem
pour que le travail soit fait dans un ThreadPool
fil et puis déplacez-vous sur la file d'attente de la prochaine TcpClient
dans sa file d'attente. Recommanderiez-vous cette approche, ou notre problème est que nous sommes à l'aide de TcpListener
et aucune quantité de rapide dequeueing est à résoudre ce problème?
Essayez cela avec plus d'un "carnet de commandes" valeur: msdn.microsoft.com/en-us/library/5kh8wf6s(v=VS.100).aspx
MSDN dit que le TcpListener va lancer une exception socketexception si la taille de file d'attente est dépassé, je n'ai pas vu de SocketExceptions (le CheckBuffer méthode de journaux), donc j'ai supposé que je n'avais pas atteint sa limite de taille encore. Mais c'est un bon point de re 1k connexions.
Juste pour préciser, ces SocketExceptions sont jetés sur le client, pas sur le serveur.
OriginalL'auteur Matthew Brindley | 2010-04-30
Vous devez vous connecter pour publier un commentaire.
J'ai préparé un code qui utilise des sockets directement, mais je n'ai pas les moyens d'effectuer un test de charge avec plus de 1000 clients. Pourriez-vous s'il vous plaît essayer de tester le fonctionnement de ce code se compare à votre solution actuelle? Je serais très intéressé par les résultats que je suis en train de construire un serveur qui doit accepter un grand nombre de connexions ainsi maintenant.
+1 Ce n'est plus en ligne avec la façon dont je le fais. En bref, ne comptez pas sur votre thread principal au début de chaque asynchrone accepter l'opération. Le coup d'envoi asynchrone accepter de le thread principal de la premier temps puis par la suite, chaque asynchrone accepter à partir de la méthode de rappel lui-même.
OriginalL'auteur dtb
Sauf si je suis en manque de quelque chose, vous appelez BeingAcceptTcpClient, qui est asynchrone, mais alors vous êtes en appelant WaitOne() pour attendre jusqu'à ce que le code asynchrone finitions , ce qui rend le processus de synchronisation. Votre code ne peut accepter qu'un seul client à la fois. Ou suis-je totalement fou? À tout le moins, il semble que beaucoup de commutation de contexte pour rien.
Vous pouvez appeler à l'écoute.BeginAcceptTcpClient à la fin de DoAcceptTcpClientCallback (après avoir appelé à l'écoute.EndAcceptTcpClient). Le ManualResetEvent n'est pas nécessaire. Mais au début/EndAcceptTcpClient méthodes ont certains problèmes de performances, c'est pourquoi la AcceptAsync méthode a été ajoutée à la classe Socket.
Ah ok, je vois. Je ne suis pas sûr que l'appel de recommencer vs à l'aide de la ManualResetEvent permettrait d'améliorer les performances? Je l'ai changé maintenant, pour enlever la ManualResetEvent et je suis d'appeler Commencer après la Fin, mais avant de le pool de threads de trucs, je vais voir comment cela fonctionne.
OriginalL'auteur Jonathan Beerhalter
Il a été fait allusion, dans les autres questions, mais je voudrais suggérer à votre tcpListener.Méthode Start (), utilisez la surcharge qui permet de régler l'arriéré à un nombre plus élevé que le nombre maximal de connexions, vous vous attendez à un moment:
Fondamentalement, cette option définit le nombre de "en attente" connexions TCP sont autorisés qui sont en attente d'Accepter d'être appelé. Si vous n'êtes pas d'accepter une connexion assez rapide, et de ce carnet de commandes se remplit, les connexions TCP sera automatiquement rejetée, et vous n'aurez même pas obtenir une chance de les traiter.
Comme d'autres l'ont mentionné, l'autre possibilité est d'accélérer la rapidité à traiter les connexions entrantes. Vous avez encore, cependant, le carnet de commandes d'une valeur plus élevée, même si vous pouvez accélérer la vitesse d'accepter le temps.
OriginalL'auteur Steve Wranovsky
Juste une suggestion : pourquoi ne pas accepter les clients de façon synchrone (en utilisant
AcceptTcpClient
au lieu deBeginAcceptTcpClient
), puis de traiter le client sur un nouveau thread ? De cette façon, vous n'aurez pas à attendre pour un client d'être traités avant de pouvoir accepter la suivante.C'est downvoted parce que, comme l'a souligné Thomas, de faire les choses de manière synchrone en termes de AcceptTcpClient nécessite 1 thread par client. Par défaut .NET va pour attribuer automatiquement des 1 mo par nouveau thread. Ce n'est pas à l'échelle.
Vrai, il suffit donc de remplacer "le processus et le client sur un nouveau fil de discussion" par "processus le client dans un pool de threads" et la réponse est de 100% de fines. Je pense que le point ici est vraiment que TcpListener peut parfaitement échelle w/o méthodes Asynchrones. Aussi, les gens peuvent cesser de recourir retour à la classe Socket à chaque fois que quelqu'un essaie d'utiliser la classe TcpListener, comme l'on a accepté la réponse. Cette réponse correspond à la question: stackoverflow.com/questions/5339782/...
OriginalL'auteur Thomas Levesque
La première chose que vous devez vous poser est "est de 1000 connexions tout à la fois raisonnable". Personnellement, je pense qu'il est peu probable que vous obtiendrez dans cette situation. Plus probablement, vous avez 1000 connexions sur une courte période de temps.
J'ai un TCP programme de test que j'utilise pour tester mon serveur de cadre, il peut faire des choses comme X connexions au total dans des lots de Y avec un écart de Z ms entre chaque lot; que je trouve personnellement est monde plus réel que le 'grand nombre tout à la fois'. C'est gratuit, cela peut aider, vous pouvez l'obtenir à partir d'ici: http://www.lenholgate.com/blog/2005/11/windows-tcpip-server-performance.html
Comme d'autres l'ont dit, l'augmentation de l'écouter carnet de commandes, processus le plus rapidement les connexions, l'utilisation asynchrone accepte si possible...
et est de 1000 connexions tout à la fois quelque chose que vous attendez vraiment à se produire dans la production?
OriginalL'auteur Len Holgate