2009-07-27 13 views
2

Je développe une application serveur en C#. Les clients peuvent se connecter au serveur et faire diverses demandes. Actuellement, lorsqu'un client se connecte, je crée un nouveau thread pour gérer la ou les requêtes. J'utilise la classe TCPClient pour gérer les connexions client. Mon serveur fonctionne comme suit:Problème de blocage Read()

  1. client se connecter au serveur avec une demande
  2. demande serveur gère
  3. Server attend de voir si le client a d'autres demandes
  4. Si le client ne fait pas une autre demande dans certains délai d'attente, le serveur tue la connexion

Mon problème est le suivant:

Lorsque je lis à partir de l'objet NetworkStream que je reçois de la classe TCPClient, la méthode NetworkStream Read() ne bloque pas s'il n'y a pas de données disponibles. Lorsque le serveur atteint l'étape n ° 3, je voudrais définir un délai d'attente sur le NetworkStream et si le client ne fait plus de demandes pendant cette durée, le serveur devrait tuer la connexion lorsque cette exception de délai d'attente est levée. Lorsque mon serveur atteint l'étape 3, la méthode NetworkStream Read() ne bloque pas, quelle que soit la définition de la propriété ReadTimeout. Quelqu'un peut-il m'aider, ou proposer une meilleure façon de faire ce que je suis en train de faire.

Merci

+1

Pouvez-vous utiliser à la place UDP et faire connexion? – Amy

Répondre

0

Vous devriez obtenir leur diplôme d'utiliser Read() à la méthode BeginRead() et regarder dans le traitement parallèle. La programmation réseau n'est pas pour les faibles de cœur.

Fondamentalement, vous ne voulez pas créer une interrogation de boucle pour les données.

while(!timedout && !stream.DataAvailable) sleep(0); // This is bad. 
if(steam.DataAvailable) steam.Read(); 

Au lieu de cela, il est plus logique de faire quelque chose comme ça. Créez un événement de réinitialisation, appelez BeginRead() et relâchez l'événement de réinitialisation sur le rappel. Quelque chose comme ce qui suit:

void clientThread() 
{ 
    stream.BeginRead(myCallback); 
    resetEvent.WaitOne(timeout) 
} 
void myCallback 
{ 
    resetEvent.Set(); 
} 
+0

Il génère déjà un nouveau thread par client, auquel cas l'utilisation de BeginRead a moins de sens .... –

+0

Il est plus logique d'utiliser sleep. http://msmvps.com/blogs/peterritchie/archive/2007/04/26/thread-sleep-is-a-sign-of-a-poorly-designed-program.aspx –

+0

Spencer: J'ai édité ma réponse pour ajuster en utilisant un waithandle, mais dans ce cas, je ne crois pas nécessairement que le sommeil est une mauvaise chose - cela dépend vraiment si le temps passé est critique ou non (puisque l'argument principal de Richie est que SleepEx ne fournit aucun timeout ou garantie Je reviendrai dans le temps que vous demanderez. –

-1

Vous devrez gérer vous-même « délai d'attente », puisque NetworkStream.Read() ne fournit pas une façon de le faire. Ce que vous voudrez probablement faire dans votre thread client est d'utiliser une combinaison de NetworkStream.DataAvailable et/ou NetworkStream.CanRead en conjonction avec un mécanisme de blocage/timeout (comme utiliser un appel WaitHandle.WaitOne avec un timeout)) pour donner au client le temps de rendre les données disponibles. Si rien ne vient après une durée spécifiée, arrêtez simplement la connexion et quittez. Entourez le NetworkStream dans un BufferedStream.

+0

Ahem. http://msmvps.com/blogs/peterritchie/archive/2007/04/26/thread-sleep-is-a-sign-of-a-poorly-designed-program.aspx –

+0

Je ne suis pas toujours d'accord avec Peter Les arguments de Richie sont là, en particulier dans les threads construits par l'utilisateur. Il a même (dans la discussion) préconise l'utilisation de Thread.Sleep (1) dans certains cas. Cela étant dit, dans ce cas, je pense que l'utilisation d'un WaitHandle est probablement un meilleur design, alors j'ai changé ma réponse. –

+0

Si le thread client attend des données mais utilise uniquement la méthode Read(), il doit être en boucle jusqu'à ce que les données soient disponibles. Si vous n'utilisez pas Sleep() avec Read(), c'est encore pire. –

0

BufferedStream.Read bloquera jusqu'à ce qu'au moins 1 octet soit disponible.

+0

Il semble que je ne puisse pas définir une durée de timeout sur un BufferedStream enroulé autour d'un NetworkStream (System.InvalidOperationException: les timeouts ne sont pas supportés sur ce flux ...) Donc, je devrais encore gérer le timeout manuellement si je le faisais route –

+0

Minuteries d'installation pour gérer les délais d'attente. Lorsque la minuterie ferme le socket, une exception sera lancée sur la méthode de lecture. Ensuite, lorsque les données sont lues, réinitialisez la minuterie. – Charlie

2

NetworkStream.Read et ReadByte doivent se bloquer jusqu'à expiration du délai s'il n'y a pas de données à lire. Je pense qu'il est beaucoup plus probable que l'appel lise quelque chose, probablement un tas de zéros. Vérifiez les données envoyées par le client avec soin.

+0

Je pense tout de même. –

0

Pour forcer explicitement NetworkStream à bloquer lorsqu'il n'y a pas de données disponibles:

TcpClient clientConnection = new TcpClient(); 

if(clientConnection.Available > 0) 
{ 
    //You want to continue execution even though there is no available data to be read from the server then: 
    clientConnection.Client.Blocking = false; 
} 
Questions connexes