51 votes

<WSACancelBlockingCall exception>

D'accord, j'ai une exception étrange lancée par mon code qui me dérange depuis des siècles.

System.Net.Sockets.SocketException: A blocking operation was interrupted by a call to WSACancelBlockingCall
   at System.Net.Sockets.Socket.Accept()
   at System.Net.Sockets.TcpListener.AcceptTcpClient()

MSDN n'est pas d'une grande aide sur ce point : http://msdn.microsoft.com/en-us/library/ms741547(VS.85).aspx et je ne sais même pas par où commencer pour résoudre ce problème. Il se produit seulement 4 ou 5 fois par jour, et jamais dans notre environnement de test. Uniquement sur les sites de production, et sur TOUS les sites de production.

J'ai trouvé plein de publications posant des questions sur cette exception, mais aucune réponse définitive sur ce qui la provoque, et comment la gérer ou l'éviter.

Le code s'exécute dans un thread d'arrière-plan séparé, la méthode démarre :

public virtual void Startup()
    {
     TcpListener serverSocket= new TcpListener(new IPEndPoint(bindAddress, port));    
        serverSocket.Start();

ensuite, je boucle en mettant toutes les nouvelles connexions en tant que tâches dans un pool de threads séparé. Cela devient plus compliqué en raison de l'architecture de l'application, mais en gros :

   while (( socket = serverSocket.AcceptTcpClient()) !=null) //Drôle d'exception ici
    {
         connectionHandler = new ConnectionHandler(socket, mappingStrategy);
         pool.AddJob(connectionHandler);
    }
  }

À partir de là, le pool a ses propres threads qui s'occupent de chaque tâche dans leur propre thread, séparément.

Je comprends qu'AcceptTcpClient() est un appel bloquant, et que d'une manière ou d'une autre, winsock dit au thread d'arrêter de bloquer et de continuer l'exécution.. mais pourquoi? Et que suis-je censé faire? Juste attraper l'exception et l'ignorer?


Eh bien, je pense qu'un autre thread ferme le socket, mais ce n'est certainement pas de mon code. Ce que j'aimerais savoir, c'est : est-ce que ce socket est fermé par le client connecté (de l'autre côté du socket) ou est-ce fermé par mon serveur. Parce que tel que c'est actuellement, chaque fois que cette exception se produit, cela ferme mon port d'écoute, fermant ainsi efficacement mon service. Si cela se produit à distance, c'est un gros problème.

Alternativement, cela pourrait-il être simplement le serveur IIS arrêtant mon application, et annulant ainsi tous mes threads d'arrière-plan et méthodes bloquantes?

52voto

TimK Points 1881

Est-il possible que le serverSocket soit fermé à partir d'un autre thread? Cela causera cette exception.

0 votes

Dites-le. Comment le fermez-vous depuis un autre thread? Le socket doit-il être 'volatile'?

3 votes

Non, tant que l'autre thread a une référence à l'objet socket, il peut le fermer.

7voto

Ceci est ma solution d'exemple pour éviter WSAcancelblablabla: Définissez votre thread comme global puis vous pouvez utiliser la méthode invoke de cette manière:

private void closinginvoker(string dummy)
    {
        if (InvokeRequired)
        {
            this.Invoke(new Action(closinginvoker), new object[] { dummy });
            return;
        }
        t_listen.Abort();
        client_flag = true;
        c_idle.Close();
        listener1.Stop();
    }

Après l'avoir invoqué, fermez d'abord le thread, puis le flag de boucle infinie afin qu'il bloque toute nouvelle attente (si vous en avez une), puis fermez le tcpclient et arrêtez l'écouteur.

0 votes

InvokeRequired lie votre solution à winforms. Et si le code s'exécute dans un service Windows?

6voto

Cela pourrait se produire sur un serverSocket.Stop(). Que j'appelais chaque fois que Dispose était appelé.

Voici à quoi ressemblait ma gestion d'exceptions pour le thread d'écoute :

try
{
    //...
}
catch (SocketException socketEx)
{    
    if (_disposed)
        ar.SetAsCompleted(null, false); //exception car le listener s'est arrêté (disposé), ignorer l'exception
    else
        ar.SetAsCompleted(socketEx, false);
}

Maintenant, ce qui se passait, c'est que de temps en temps, l'exception se produisait avant que _disposed ne soit défini sur vrai. Donc la solution pour moi a été de rendre tout conforme aux threads.

3voto

blogga Points 11

Pareil ici! Mais j'ai découvert que le ReceiveBuffer du côté serveur était inondé par les clients! (Dans mon cas, un tas de scanners RFID qui continuaient de spammer le TagCode au lieu de cesser d'envoyer avant l'arrivée du prochain TagCode)

Cela a aidé d'augmenter les ReceiveBuffers et de reconfigurer les scanners...

0voto

SimonNZ Points 1

Plus récemment, j'ai vu cette exception lors de l'utilisation de HttpWebRequest pour mettre en ligne un gros fichier et que la période de délai était dépassée.

En utilisant le code suivant tant que votre temps de mise en ligne > 3 secondes, cela causera cette erreur autant que je puisse voir.

chaîne chemin = "Fichier de taille raisonnable.dat";
int tailleDuTampon = 1024;
byte[] tampon = new byte[tailleDuTampon];
System.Net.HttpWebRequest req = (HttpWebRequest)System.Net.HttpWebRequest.Create("Some URL");
req.Method = "PUT";
req.Timeout = 3000; //3 secondes, petit délai pour démonstration
long longueur = new System.IO.FileInfo(chemin).Length;
à l'aide (FileStream entrée = File.OpenRead(chemin))
{
    using (Stream sortie = req.GetRequestStream())
    {
        long restant = longueur;
        int octetsLus = 0;
        tant que ((octetsLus = entrée.Read(tampon, 0, (int)Math.Min(restant, (decimal)tailleDuTampon))) > 0)
        {
            sortie.Write(tampon, 0, octetsLus);
            restant -= octetsLus;
        }
        sortie.Close();
    }
entrée.Close();
}

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