Lenteur .NET NetworkStream Read

J’ai un code de réseau pour traiter une connexion TCP arbitraire.

Tout semble fonctionner comme prévu, mais semble lent. Une fois que j’ai profilé le code, il semble que nous avons dépensé pas moins de 600 ms dans NetworkStream.Read () et je me demande comment l’améliorer. J’ai manipulé les tailles de mémoire tampon et alterné entre une mémoire tampon massive pour lire toutes les données en une fois ou une petite pour concaténer les données dans un SsortingngBuilder. Actuellement, le client que j’utilise est un navigateur Web, mais ce code est générique et il se peut que ce ne soit pas des données HTTP qui lui sont envoyées. Des idées?

Mon code est le suivant:

public void StartListening() { try { lock (oSyncRoot) { oTCPListener = new TcpListener(oIPaddress, nPort); // fire up the server oTCPListener.Start(); // set listening bit bIsListening = true; } // Enter the listening loop. do { // Wait for connection TcpClient newClient = oTCPListener.AcceptTcpClient(); // queue a request to take care of the client oThreadPool.QueueUserWorkItem(new WaitCallback(ProcessConnection), newClient); } while (bIsListening); } catch (SocketException se) { Logger.Write(new TCPLogEntry("SocketException: " + se.ToSsortingng())); } finally { // shut it down StopListening(); } } private void ProcessConnection(object oClient) { TcpClient oTCPClient = (TcpClient)oClient; try { byte[] abBuffer = new byte[1024]; SsortingngBuilder sbReceivedData = new SsortingngBuilder(); using (NetworkStream oNetworkStream = oTCPClient.GetStream()) { // set initial read timeout to nInitialTimeoutMS to allow for connection oNetworkStream.ReadTimeout = nInitialTimeoutMS; int nBytesRead = 0; do { try { bool bDataAvailable = oNetworkStream.DataAvailable; while (!bDataAvailable) { Thread.Sleep(5); bDataAvailable = oNetworkStream.DataAvailable; } nBytesRead = oNetworkStream.Read(abBuffer, 0, abBuffer.Length); if (nBytesRead > 0) { // Translate data bytes to an ASCII ssortingng and append sbReceivedData.Append(Encoding.UTF8.GetSsortingng(abBuffer, 0, nBytesRead)); // decrease read timeout to nReadTimeoutMS second now that data is coming in oNetworkStream.ReadTimeout = nReadTimeoutMS; } } catch (IOException) { // read timed out, all data has been resortingeved nBytesRead = 0; } } while (nBytesRead > 0); //send the data to the callback and get the response back byte[] abResponse = oClientHandlerDelegate(sbReceivedData.ToSsortingng(), oTCPClient); if (abResponse != null) { oNetworkStream.Write(abResponse, 0, abResponse.Length); oNetworkStream.Flush(); } } } catch (Exception e) { Logger.Write(new TCPLogEntry("Caught Exception " + e.StackTrace)); } finally { // stop talking to client if (oTCPClient != null) { oTCPClient.Close(); } } } 

Edit: Je reçois à peu près les mêmes chiffres sur deux machines entièrement séparées (ma machine de développement XP et une boîte 2003 dans une colo). J’ai mis du temps dans le code autour des parties pertinentes (à l’aide de System.Diagnostic.StopWatch) et le vide dans un journal:

 06/07/2009 15:44:50: Débogage: Alors que DataAvailable a pris 0 ms
 06/07/2009 15:44:50: Le débogage: La lecture a pris 531 ms
 06/07/2009 15:44:50: Débogage: ProcessConnection a pris 577 ms

Je vous recommande d’utiliser le Moniteur réseau Microsoft ou quelque chose du genre pour voir ce qui se passe en termes de 600 ms. NetworkStream est un logiciel de gestion de réseau. Lorsque vous examinez son comportement, tenez toujours compte de ce que fait le réseau.

Un autre vote pour l’utilisation d’un logiciel de surveillance du réseau. Moniteur réseau ou WireShark devrait faire l’affaire. Assurez-vous d’enregistrer l’heure à laquelle l’appel networkstream.read commence et se termine dans votre programme afin de savoir où se trouvent les événements du programme sur le trafic réseau enregistré.

Nous vous recommandons également d’attendre que la propriété NetworkStream.DataAvailable devienne vraie avant d’appeler la méthode Read et d’enregistrer l’heure à laquelle elle devient vraie. Si votre moniteur réseau indique que les données arrivent 600 ms avant que votre programme ne puisse être lu, il se peut que quelque chose sur votre ordinateur retienne le paquet, par exemple un antivirus ou votre pare-feu.

Addendum 2009/7/6 3:12 PM EDT:

Les informations de chronométrage supplémentaires que vous avez publiées sont intéressantes. Si des données sont disponibles, pourquoi la lecture est-elle si longue? J’ai exécuté votre code sur ma machine de développement, et les deux en attente de données disponibles et la fonction de lecture elle-même sort à 0 milliseconde. Êtes-vous sûr de disposer des derniers Service Packs, etc. installés? J’exécute Visual Studio Professional 2005 avec .NET 2.0.50727. J’ai également installé .NET 3.0 et 3.5, mais je ne pense pas que VS 2005 les utilise. Avez-vous une nouvelle installation de système d’exploitation (machine réelle ou virtuelle) sans programme supplémentaire (même / surtout ceux “requirejs” par le service informatique de l’entreprise) sur laquelle vous pouvez essayer?

Voici le code que j’ai couru:

 using System; using System.Collections.Generic; using System.Text; using System.Net; using System.Net.Sockets; using System.IO; using System.Threading; using System.Diagnostics; namespace stackoverflowtest { class Program { static private object oSyncRoot = new object(); static private TcpListener oTCPListener; static private IPAddress oIPaddress = IPAddress.Parse("10.1.1.109"); static private int nPort = 8009; static bool bIsListening = true; static void Main(ssortingng[] args) { StartListening(); Thread.Sleep(500000); bIsListening = false; } public static void StartListening() { try { lock (oSyncRoot) { oTCPListener = new TcpListener(oIPaddress, nPort); // fire up the server oTCPListener.Start(); // set listening bit bIsListening = true; } // Enter the listening loop. do { // Wait for connection TcpClient newClient = oTCPListener.AcceptTcpClient(); // queue a request to take care of the client ThreadPool.QueueUserWorkItem(new WaitCallback(ProcessConnection), newClient); } while (bIsListening); } catch (SocketException se) { Console.WriteLine("SocketException: " + se.ToSsortingng()); } finally { // shut it down //StopListening(); } } private static void ProcessConnection(object oClient) { TcpClient oTCPClient = (TcpClient)oClient; try { byte[] abBuffer = new byte[1024]; SsortingngBuilder sbReceivedData = new SsortingngBuilder(); using (NetworkStream oNetworkStream = oTCPClient.GetStream()) { int nInitialTimeoutMS = 1000; // set initial read timeout to nInitialTimeoutMS to allow for connection oNetworkStream.ReadTimeout = nInitialTimeoutMS; int nBytesRead = 0; do { try { bool bDataAvailable = oNetworkStream.DataAvailable; Stopwatch sw = new Stopwatch(); while (!bDataAvailable) { Thread.Sleep(5); bDataAvailable = oNetworkStream.DataAvailable; } Console.WriteLine("DataAvailable loop took " + sw.ElapsedMilliseconds); sw.Reset(); nBytesRead = oNetworkStream.Read(abBuffer, 0, abBuffer.Length); Console.WriteLine("Reading " + nBytesRead + " took " + sw.ElapsedMilliseconds); if (nBytesRead > 0) { // Translate data bytes to an ASCII ssortingng and append sbReceivedData.Append(Encoding.UTF8.GetSsortingng(abBuffer, 0, nBytesRead)); // decrease read timeout to nReadTimeoutMS second now that data is coming in int nReadTimeoutMS = 100; oNetworkStream.ReadTimeout = nReadTimeoutMS; } } catch (IOException) { // read timed out, all data has been resortingeved nBytesRead = 0; } } while (nBytesRead > 0); byte[] abResponse = new byte[1024]; for (int i = 0; i < abResponse.Length; i++) { abResponse[i] = (byte)i; } oNetworkStream.Write(abResponse, 0, abResponse.Length); oNetworkStream.Flush(); //send the data to the callback and get the response back //byte[] abResponse = oClientHandlerDelegate(sbReceivedData.ToString(), oTCPClient); //if (abResponse != null) //{ // oNetworkStream.Write(abResponse, 0, abResponse.Length); // oNetworkStream.Flush(); //} } } catch (Exception e) { Console.WriteLine("Caught Exception " + e.StackTrace); } finally { // stop talking to client if (oTCPClient != null) { oTCPClient.Close(); } } } } } 

Après quelques recherches supplémentaires, il semble que le seul moyen d’accélérer cette opération est de rompre après la lecture des x premiers octets. Le délai semble être à la deuxième lecture. Si je change le tampon en 8096 octets (probablement le maximum que mon application sera envoyé à tout moment) et casser ici:

  if (nBytesRead > 0) { // Translate data bytes to an ASCII ssortingng and append sbReceivedData.Append(Encoding.UTF8.GetSsortingng(abBuffer, 0, nBytesRead)); if (bTurboMode) { break; } else { // decrease read timeout to nReadTimeoutMS second now that data is coming in oNetworkStream.ReadTimeout = nReadTimeoutMS; } } 

Ensuite, le temps de réponse passe de 600 ms à environ 80 ms. C’est une solution acceptable pour moi actuellement. Je peux basculer le bTurboMode à partir de l’application appelante et accélérer considérablement les choses pour ce cas