web-dev-qa-db-fra.com

Random "Une connexion existante a été fermée de force par l'hôte distant." après un TCP réinitialiser

J'ai deux parties, un client et un serveur. Et j'essaie d'envoyer des données (taille> 5840 octets) du client au serveur, qui les renvoie ensuite. Je boucle ceci un certain nombre de fois en attendant une seconde entre chaque fois. Parfois, l’application du serveur se bloquait, mais l’erreur semblait très aléatoire:

Exception non gérée: System.IO.IOException: Impossible de lire les données à partir de la connexion de transport: une connexion existante a été fermée de force par l'hôte distant. --->

System.Net.Sockets.SocketException: une connexion existante a été fermée de force par l'hôte distant

sur System.Net.Sockets.Socket.Receive (tampon Byte [], décalage Int32, taille Int32, SocketFlags socketFlags)

sur System.Net.Sockets.NetworkStream.Read (tampon Byte [], décalage Int32, Int32 à la taille)

--- Fin de trace de pile d'exception interne ---

sur System.Net.Sockets.NetworkStream.Read (tampon Byte [], décalage Int32, Int32 à la taille)

sur TCP_Server.Program.Main (String [] args)

Code client (à l'intérieur de la boucle):

            try
            {
                Int32 port = 13777;
                using (TcpClient client = new TcpClient(ip, port))
                using (NetworkStream stream = client.GetStream())
                {
                    client.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.KeepAlive, true);
                    var data = GenerateData(size);

                    sw.Start();
                    // Send the message to the connected TcpServer. 
                    stream.Write(data, 0, data.Length);

                    // Buffer to store the response bytes.
                    data = new Byte[size];

                    // Read the first batch of the TcpServer response bytes.
                    Int32 bytes = stream.Read(data, 0, data.Length);

                    sw.Stop();
                    Console.WriteLine(i + ": Done transporting " + size + " bytes to and from " + ip + " time: " +
                                      sw.ElapsedMilliseconds + " ms");
                    // Close everything.
                    stream.Close();
                    client.Close();
                }

            }
            catch (ArgumentNullException e)
            {
                Console.WriteLine("ArgumentNullException: {0}", e);
            }
            catch (SocketException e)
            {
                Console.WriteLine("SocketException: {0}", e);
            }

            sw.Reset();

Code serveur: 

            Byte[] bytes = new Byte[size];

            // Enter the listening loop. 
            for (int i = 0; i < numberOfPackages; i++)
            {
                using (TcpClient client = server.AcceptTcpClient())
                using (NetworkStream stream = client.GetStream())
                {
                    client.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.KeepAlive, true);
                    // Loop to receive all the data sent by the client. 
                    while ((stream.Read(bytes, 0, bytes.Length)) != 0)
                    {
                        // Send back a response.
                        stream.Write(bytes, 0, size);

                    }
                    client.GetStream().Close();
                    client.Close();
                }


                Console.WriteLine("Receive data size " + size);

            }

J'ai utilisé Wirehark pour surveiller les paquets TCP envoyés et j'ai constaté qu'un RST TCP est envoyé du client au serveur avant que le programme ne se bloque. Donc, je suppose que le problème est que le RST n’est pas traité correctement . Il n’existe pas de pare-feu entre le client et l’hôte, ce n’est donc pas le problème.

Les fichiers Wireshark pour le client et le serveur sont ici: https://www.dropbox.com/sh/ctl2chq3y2c20n7/AACgIJ8IRiclqnyOyw8sqd9La?dl=0

Donc, soit je dois me débarrasser de la taxe RST TCP, soit j'ai besoin de mon serveur pour le gérer d'une manière ou d'une autre et pour ne pas planter.

J'ai essayé d'utiliser un temps d'attente plus long, mais cela n'aide pas… .. Si les données sont inférieures à 5840 octets, je ne reçois aucun plantage, ce que je sais.

Des suggestions ou des idées?

EDIT: Grâce aux réponses apportées, je l’ai obtenu avec les modifications suivantes:

Serveur:

// Loop to receive all the data sent by the client.
int k = 0;
while (k < size)
{
   int bytesRead = stream.Read(bytes, 0, bytes.Length);
   k += bytesRead;
}
// Send back a response.
stream.Write(bytes, 0, size);

Et la même chose lors de la réception côté client. Depuis que je veux d’abord envoyer toutes les données, puis laisser le serveur répondre, cela fonctionne pour mon application.

11
TobiasW

Deux problèmes que je vois:

  1. Vous supposez qu'une lecture pour size octets lira en fait size octets. Ce ne est pas. TCP est un protocole de diffusion en continu. Une lecture lit au moins un octet. C'est la seule garantie donnée.
  2. Votre code sera bloqué au hasard. Le client écrit des données sur le serveur. Le serveur lui répond. Mais le client ne lira pas avant d'avoir tout écrit. S'il écrit plus que les tampons réseau ne peuvent en supporter, cela entraînera un blocage. Le client doit lire et écrire simultanément. Probablement, vous avez besoin d'un autre thread/tâche pour relire les données. Une bonne solution pour résoudre ce problème consisterait à démarrer une tâche de rédacteur, une tâche de lecteur et à Task.WhenAll/WaitAll de les rejoindre.

Je ne sais pas dans quelles circonstances exactes une pile TCP enverrait un RST. Pourrait être dû à l'impasse provoquant un délai d'attente.

Vous n’avalez aucune exception, non?

Normalement, la fermeture d'une connexion entraîne un arrêt ordonné en arrière-plan. Mais je ne suis pas sûr de ce qui se passe lorsque l’autre partie écrit encore à ce stade. Peut-être que la réponse est qu'une RST est générée par le destinataire des écritures. La spécification TCP répondrait à cette question. Si cette réponse doit être approuvée, alors un arrêt (lecture)/fermeture suivi d'une écriture entrante entraînerait la connexion RST.

Résolvez les deux problèmes et faites un rapport avec vos conclusions.

8
usr

J'ai résolu le même problème avec l'augmentation de la limite de cache de l'entrée IIS root. vous pouvez également désactiver la mise en cache. J'espère que ça aide.

0
user2504655

Nous avons eu ce problème en utilisant des erreurs aléatoires HttpClient comme

System.Net.Http.HttpRequestException: The SSL connection could not be established, see inner exception.
 ---> System.IO.IOException: Unable to read data from the transport connection: An existing connection was forcibly closed by the remote Host.
 ---> System.Net.Sockets.SocketException: An existing connection was forcibly closed by the remote Host

ou

 ---> System.Net.WebException: The underlying connection was closed: An unexpected error occurred on a send.

Problème dans la notification explicite de congestion, ou ECN, dans la pile TCP. Ceci est également connu sous le nom de capacité ECN. La notification explicite de congestion est une extension du protocole Internet et du protocole de contrôle de transmission et est définie dans la RFC 3168. ECN permet la notification de bout en bout de la congestion du réseau sans perdre de paquets. Si la fonctionnalité ECN est activée, vous pouvez la désactiver:

netsh interface tcp set global ecncapability=disabled

Un redémarrage n'est pas nécessaire.

0
Daniil Sokolyuk

Je ne suis pas un expert, mais ne pensez-vous pas qu'appeler stream.Close () sur le client ferme le flux alors que le serveur tente toujours d'écrire sur 

// Send back a response.
stream.Write(bytes, 0, size);

Aussi, vous voudrez peut-être mettre quelques données pour marquer la fin, de sorte que le serveur sache et arrête la lecture.

0
user2527768