68 votes

Manière correcte d'arrêter TcpListener

Je suis actuellement en utilisant TcpListener à l'adresse des connexions entrantes, chacun de qui sont donnés un thread pour la manipulation de la communication, puis de l'arrêt que la connexion unique. Le Code se présente comme suit:

TcpListener listener = new TcpListener(IPAddress.Any, Port);
System.Console.WriteLine("Server Initialized, listening for incoming connections");
listener.Start();
while (listen)
{
     // Step 0: Client connection
     TcpClient client = listener.AcceptTcpClient();
     Thread clientThread = new Thread(new ParameterizedThreadStart(HandleConnection));
     clientThread.Start(client.GetStream());
     client.Close();
}

L' listen variable est un booléen qui est un champ de la classe. Maintenant, lorsque le programme s'arrête, je veux cesser d'écouter les clients. Réglage écoutez false l'empêcher de prendre plus de connexions, mais depuis AcceptTcpClient est un appel bloquant, il aura au minimum prendre le client suivant, PUIS sur quitter. Est-il un moyen de le forcer à simplement sortir et de s'arrêter, là, tout de suite? Quels sont les effets de l'appel de l'auditeur.Stop() ont tandis que l'autre appel de blocage est en cours d'exécution?

65voto

Peter Oehlert Points 6351

Il y a 2 suggestions je ferais donné le code et ce que je suppose est votre conception. Cependant, je tiens à souligner tout d'abord que vous devriez vraiment utiliser, non-blocage I/O rappels lorsque vous travaillez avec des I/O comme réseau ou des systèmes de fichiers. Il est de loin BEAUCOUP plus efficace et que votre application fonctionnera beaucoup mieux si elles sont plus difficiles à programmer. J'aborderai brièvement une suggestion de modification de la conception à la fin.

  1. Utilisez la fonction(){} pour TcpClient
  2. Fil de discussion.Abort()
  3. TcpListener.En attendant()
  4. Asynchrone de réécriture

Utilisez la fonction(){} pour TcpClient

*** Notez que vous devriez vraiment joignez à votre TcpClient appel à une aide(){} bloc pour s'assurer que TcpClient.Dispose() ou TcpClient.Close() les méthodes sont appelées, même dans le cas d'une exception. Alternativement, vous pouvez mettre ceci dans le bloc finally d'un try {} finally {} bloc.

Fil de discussion.Abort()

Il y a 2 choses que je vois que vous pourriez faire. 1, c'est que si vous avez commencé ce TcpListener fil à partir d'un autre, vous pouvez simplement appeler Fil.Abandon de la méthode d'instance sur le fil qui va provoquer un threadabortexception être jeté dans l'appel de blocage et de marcher jusqu'à la pile.

TcpListener.En attendant()

La deuxième à faible coût solution serait d'utiliser l'auditeur.Dans l'attente de() la méthode à mettre en œuvre un modèle de bureau de vote. Ensuite, vous devez utiliser un Fil.Le sommeil à "attendre" avant de voir si une nouvelle connexion est en attente. Une fois que vous avez une attente de connexion que vous souhaitez appeler AcceptTcpClient et qui libèrent la connexion en attente. Le code devrait ressembler à quelque chose comme ça.

while (listen){
     // Step 0: Client connection
     if (!listener.Pending())
     {
          Thread.Sleep(500); // choose a number (in milliseconds) that makes sense
          continue; // skip to next iteration of loop
     }

     TcpClient client = listener.AcceptTcpClient();
     Thread clientThread = new Thread(new ParameterizedThreadStart(HandleConnection));
     clientThread.Start(client.GetStream());
     client.Close();
}

Asynchrone De Réécriture

Enfin, je vous recommande vraiment de passer à un non-blocage de la méthodologie pour votre application. Sous les couvertures le cadre utilisera Overlapped I/O et I/O ports de fin pour mettre en œuvre les non-blocage I/O à partir de vos appels asynchrones. Il n'est pas très difficile, il faut juste penser à votre code un peu différemment.

Fondamentalement, vous commencerez votre code avec le BeginAcceptTcpClient méthode et de garder trace de la IAsyncResult que vous êtes renvoyé. Vous point qu'à une méthode dont le responsable de l'obtention de la TcpClient et la passant PAS pour un nouveau thread, mais à un fil du pool de threads.QueueUserWorkerItem de sorte que vous n'êtes pas en rotation et la fermeture d'un nouveau thread pour chaque demande du client (veuillez Noter que vous devez utiliser votre propre pool de threads si vous avez particulièrement longtemps vécu les demandes, car le pool de threads est partagé et si vous monopoliser tous les threads d'autres parties de votre application mise en œuvre par le système peut être mis à jeun). Une fois que l'auditeur méthode a débuté votre new TcpClient à son propre pool de threads demande il appelle BeginAcceptTcpClient de nouveau et de points, le délégué de retour à lui-même.

Effectivement, vous êtes juste à la rupture de votre méthode actuelle dans 3 différentes méthodes qui seront ensuite appelés par les différentes parties. 1. pour bootstrapt everythin, 2. d'être la cible d'appeler EndAcceptTcpClient, le coup d'envoi de la TcpClient à son propre thread, puis appeler une fois de plus, de 3. pour traiter la demande du client et de le fermer lorsque vous avez terminé.

49voto

zproxy Points 1508

listener.Server.Close() d'un autre thread interrompt l'appel bloquant.

 A blocking operation was interrupted by a call to WSACancelBlockingCall
 

3voto

Dzmitry Huba Points 3333

Sockets de fournir de puissantes asynchrone capacités. Jetez un oeil à l'Aide d'un Socket Serveur Asynchrone

Voici quelques notes sur le code.

En utilisant manuellement les threads créés dans ce cas, peut-être un rétroprojecteur.

Le code ci-dessous est soumise à des conditions de course - TcpClient.Close() ferme le courant de réseau, vous obtenez par le biais de TcpClient.GetStream(). Envisager de fermer client où vous pouvez certainement dire qu'il n'est plus nécessaire.

 clientThread.Start(client.GetStream());
 client.Close();

TcpClient.Stop() ferme la socket sous-jacente. TcpCliet.AcceptTcpClient() utilise le Socket.Méthode Accept() sur une socket sous-jacente qui va jeter exception socketexception une fois qu'il est fermé. Vous pouvez appeler à partir d'un autre thread.

De toute façon je recommande sockets asynchrones.

3voto

Mike Scott Points 6062

Ne pas utiliser une boucle. Au lieu de cela, appelez BeginAcceptTcpClient () sans boucle. Dans le rappel, lancez simplement un autre appel à BeginAcceptTcpClient (), si votre indicateur d'écoute est toujours défini.

Pour arrêter l’auditeur, puisque vous n’avez pas bloqué, votre code peut simplement appeler Close () dessus.

1voto

Eric Nicholson Points 2043

Juste pour ajouter encore plus de raisons d'utiliser l'approche asynchrone, je suis assez sûr Fil.Abandonner ne fonctionne pas parce que l'appel est bloqué au niveau de l'OS pile TCP.

Aussi... si vous appelez BeginAcceptTCPClient dans le rappel à l'écoute pour chaque connexion, mais le premier, veillez à ce que le thread qui a exécuté la première BeginAccept n'est pas arrêter ou d'autre, l'auditeur sera automatiquement éliminés par le cadre. Je suppose que c'est une fonctionnalité, mais dans la pratique c'est très ennuyeux. Dans les applications de bureau, il n'est généralement pas un problème, mais sur le web, vous pourriez vouloir utiliser le pool de threads depuis les threads ne sont pas toujours vraiment mettre fin.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X