web-dev-qa-db-fra.com

httpWebRequest (La connexion sous-jacente a été fermée: La connexion a été fermée de façon inattendue.)

Je développe une application C # qui enregistre les données d'un serveur Web. Il envoie la demande de publication suivante au serveur Web et attend la réponse.

    /// <summary>
    /// Function for obtaining testCgi data 
    /// </summary>
    /// <param name="Parameters"></param>
    /// <returns></returns>
    private string HttpmyPost(string Parameters)
    {
        string str = "No response";
        HttpWebRequest request = (HttpWebRequest)WebRequest.Create(uriTestCGI);
        request.Method = "POST";

        byte[] bytes = Encoding.UTF8.GetBytes(Parameters);
        request.ContentLength = bytes.Length;

        Stream requestStream = request.GetRequestStream();
        requestStream.Write(bytes, 0, bytes.Length);
        requestStream.Close();

            WebResponse response = request.GetResponse();
            Stream stream = response.GetResponseStream();
            StreamReader reader = new StreamReader(stream);

            try
            {
                var result = reader.ReadToEnd();
            stream.Dispose();
            str = result.ToString();
            reader.Dispose();
        }
        catch (WebException ex)
        {
            //System.Windows.Forms.MessageBox.Show(ex.Message);
            System.Diagnostics.Trace.WriteLine(ex.Message);

        }
        finally
        {
            request.Abort();
        }
        return str;
    }

Je reçois l'erreur

> "The underlying connection was closed: The connection was closed
> unexpectedly"

J'ai essayé de déboguer l'erreur, et j'ai utilisé fiddler afin de vérifier la demande de publication telle que fournie par Firefox. À ma grande surprise, chaque fois que Fiddler était mon programme fonctionnait parfaitement. Lorsque je ferme le violoneux, je rencontre la même erreur.

Je soupçonne que puisque Fiddler agit en tant que proxy, il peut modifier certains paramètres. J'ai essayé d'utiliser webclient et le résultat était le même.

Lorsque j'ai essayé de coder la demande en python, tout a fonctionné comme il se doit sans aucun problème. Bien sûr, j'ai la possibilité d'installer IronPython et d'encapsuler cette fonction particulière, mais je considère que cela est exagéré et manque d'élégance, donc je poursuis une approche plus légère. Je soupçonne que ce n'est rien de plus qu'un ajustement de réglage.

J'ai essayé de modifier et dans mon cas c'est indifférent.

request.Accept 
request.ReadWriteTimeout 
request.Timeout 
request.UserAgent 
request.Headers
request.AutomaticDecompression 
request.Referer
request.AllowAutoRedirect
//request.TransferEncoding 
request.Expect
request.ServicePoint.Expect100Continue 
request.PreAuthenticate 
request.KeepAlive 
request.ProtocolVersion 
request.ContentType

Avec ou sans les ajustements ci-dessus, le code fonctionne lorsque Fiddler capture des données.

Il convient également de noter que le programme génère l'erreur à

WebResponse response = request.GetResponse();

MISE À JOUR: Suite aux suggestions @EricLaw, j'ai examiné la latence. J'ai trouvé cet article HttpWebRequest ralentit lors de l'ajout d'un intervalle qui suggérait de tourner l'algorithme Nagle. Maintenant, il n'y a pas de connexions fermées, bien qu'il y ait un petit décalage dans la réponse globale (lorsque j'utilise des Winforms, et non async).

13
NMech

J'écris un peu sur la façon dont Fiddler peut "magiquement" réparer les choses ici: http://blogs.telerik.com/fiddler/posts/13-02-28/help!-running-fiddler-fixes-my- app -

Le problème que vous rencontrez est en fait un bogue dans le .NET Framework lui-même. Les règles de HTTP sont telles que le serveur peut fermer une connexion KeepAlive à tout moment après l'envoi de la première réponse (par exemple, il n'a pas besoin d'accepter une autre demande sur la connexion, même si le client a demandé le comportement KeepAlive).

.NET a un bogue où il s'attend à ce que le serveur inclue un Connection: close en-tête de réponse s'il fermera la connexion une fois la réponse terminée. Si le serveur ferme la connexion sans le Connection: Close en-tête (entièrement valide par RFC2616), .NET rencontrera la connexion fermée lors de la tentative d'envoi de la prochaine requête sur la connexion et il lèvera cette exception. Ce que .NET devrait faire, c'est créer silencieusement une nouvelle connexion et renvoyer la demande sur cette nouvelle connexion.

Fiddler résout ce problème car il ne se soucie pas si le serveur ferme la connexion et maintient la connexion au client active. Lorsque le client envoie sa deuxième demande, Fiddler tente de réutiliser sa connexion au serveur, remarque qu'elle est fermée et crée silencieusement une nouvelle connexion.

Vous pouvez atténuer ce problème dans votre code en:

  1. Désactiver Keepalive à la demande (cela nuit aux performances)
  2. Attraper l'exception et réessayer automatiquement
  3. Changer le serveur pour maintenir les connexions en vie plus longtemps

L'approche n ° 3 ne fonctionne que si vous contrôlez le serveur et parce que le client peut être derrière une passerelle/proxy qui ferme les connexions après utilisation, vous devriez probablement utiliser l'approche n ° 2 également.

28
EricLaw

une suggestion et une question: 1) si vous voulez vraiment voir ce qui se passe, installez Wireshark. Il vous montrera exactement ce qui est envoyé/reçu. et il permettra de comparer avec le violoneux. Je suppose que vous manquez un en-tête, comme request.ContentType = "....", mais seul Wirehark vous montrera lequel (est envoyé via votre alternative de travail, alors qu'il n'est pas envoyé par votre HttpWebRequest).

2) obtenez-vous l'erreur dans le contenu de la réponse http, ou s'agit-il d'une exception, et si c'est une exception, est-elle interceptée dans votre capture, ou se produit-elle pendant la demande, avant votre instruction try?

0
Guy