.NET NetworkStream Медлительность чтения
У меня есть сетевой код для обработки произвольного TCP-соединения.
Кажется, все работает, как ожидалось, но кажется медленным. Когда я профилировал код, он, похоже, провел 600 мс в NetworkStream.Read(), и мне интересно, как его улучшить. Я поэкспериментировал с размерами буфера и чередовал большой буфер для чтения всех данных за один раз или маленький, который должен объединять данные в StringBuilder. В настоящее время клиент, которым я пользуюсь, является веб-браузером, но этот код является общим, и вполне может быть, что данные HTTP отправляются на него. Есть идеи?
Мой код такой:
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.ToString()));
}
finally
{
// shut it down
StopListening();
}
}
private void ProcessConnection(object oClient)
{
TcpClient oTCPClient = (TcpClient)oClient;
try
{
byte[] abBuffer = new byte[1024];
StringBuilder sbReceivedData = new StringBuilder();
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 string and append
sbReceivedData.Append(Encoding.UTF8.GetString(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 retrieved
nBytesRead = 0;
}
}
while (nBytesRead > 0);
//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)
{
Logger.Write(new TCPLogEntry("Caught Exception " + e.StackTrace));
}
finally
{
// stop talking to client
if (oTCPClient != null)
{
oTCPClient.Close();
}
}
}
Редактировать: я получаю примерно одинаковые цифры на двух совершенно отдельных машинах (моя машина для разработки XP и коробка 2003 в коло). Я поместил немного времени в код вокруг соответствующих частей (используя System.Diagnostic.StopWatch) и выгрузил его в журнал:
06.07.2009 15:44:50: отладка: в то время как DataAvailable заняла 0 мс 06.07.2009 15:44:50: отладка: чтение заняло 531 мс 06.07.2009 15:44:50: отладка: ProcessConnection заняла 577 мс
3 ответа
После еще одного исследования кажется, что единственный способ ускорить это - сломаться после того, как будут прочитаны первые x байтов. Задержка, кажется, на втором чтении. Если я изменю размер буфера на 8096 байт (возможно, это будет максимум, когда мое приложение будет отправлено в любой момент), то получится разрыв:
if (nBytesRead > 0)
{
// Translate data bytes to an ASCII string and append
sbReceivedData.Append(Encoding.UTF8.GetString(abBuffer, 0, nBytesRead));
if (bTurboMode)
{
break;
}
else
{
// decrease read timeout to nReadTimeoutMS second now that data is coming in
oNetworkStream.ReadTimeout = nReadTimeoutMS;
}
}
Тогда время отклика увеличивается от 600 до 80 мс. Это приемлемое решение для меня в настоящее время. Я могу переключить bTurboMode из вызывающего приложения и существенно ускорить процесс в этом случае
Я рекомендую вам использовать Microsoft Network Monitor или что-то подобное, чтобы увидеть, что происходит с этими 600 мс. NETworkStream - это часть сетевого программного обеспечения - при рассмотрении его поведения всегда учитывайте, что делает сеть.
Еще один голос за использование программного обеспечения для мониторинга сети. Либо Сетевой монитор или WireShark должны делать. Убедитесь, что вы записываете, в какое время вызов networkstream.read начинается и заканчивается в вашей программе, чтобы вы могли знать, где в зарегистрированном сетевом трафике произошли события вашей программы.
Кроме того, я бы рекомендовал подождать, пока свойство NetworkStream.DataAvailable станет истинным, прежде чем вызывать метод Read, и записать время, когда оно также станет истинным. Если ваш сетевой монитор показывает данные, поступающие за 600 мс до того, как ваша программа показывает, что они могут быть прочитаны, возможно, что-то еще на вашем компьютере задерживает пакет - например, антивирус или брандмауэр.
Приложение 2009/7/6 15:12 EDT:
Дополнительная информация о времени, которую вы разместили, интересна. Если данные доступны, почему это занимает так много времени для чтения? Я запустил ваш код на своей машине для разработки, и ожидание доступных данных и сама функция чтения выдаются за 0 миллисекунд. Вы уверены, что у вас установлены последние пакеты обновлений и т. Д.? Я использую Visual Studio Professional 2005 с.NET 2.0.50727. У меня также установлены.NET 3.0 и 3.5, но я не думаю, что VS 2005 использует их. У вас есть свежая установка ОС (реальная или виртуальная машина) без каких-либо дополнительных программ (даже / особенно тех, которые "требуются" корпоративным ИТ), на которых вы могли бы это попробовать?
Вот код, который я запустил:
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 stackrutest
{
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(string[] 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.ToString());
}
finally
{
// shut it down
//StopListening();
}
}
private static void ProcessConnection(object oClient)
{
TcpClient oTCPClient = (TcpClient)oClient;
try
{
byte[] abBuffer = new byte[1024];
StringBuilder sbReceivedData = new StringBuilder();
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 string and append
sbReceivedData.Append(Encoding.UTF8.GetString(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 retrieved
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();
}
}
}
}
}