Обзор проблемы: я уже некоторое время занимаюсь написанием пользовательских приложений на http-сервере. Я обнаружил, что при подключении любого веб-браузера к моему серверному приложению задержка 0,5-1 секунды (в соответствии с Google Chrome) перед обработкой запроса [который будет занимать миллисекунды]
В конце концов я попытался создать фиктивную программу, чтобы точно определить проблему:
using System.Text;
using System.Net;
using System.Net.Sockets;
namespace SlowHTTPServer
{
class FailServer
{
static void Main()
{
//Create socket object, bind it, listen to 80
Socket listenerSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
listenerSocket.Bind(new IPEndPoint(IPAddress.Any, 80));
listenerSocket.Listen(64);
while (true)
{
Socket clientConn = listenerSocket.Accept(); //Accept
System.DateTime startTime = System.DateTime.Now;
byte[] buffer = new byte[1024]; //Request header buffer
clientConn.Receive(buffer); //Recieve request header
string reqHeader = Encoding.ASCII.GetString(buffer); //Get the string version of it
//We completely ignore most of the request header lol
//Normally i'd send a response header but...
if (reqHeader.IndexOf("script1") != -1) //script1.js - document.title='hai dere'
clientConn.Send(Encoding.ASCII.GetBytes("document.title='hai dere';"));
else if (reqHeader.IndexOf("script2") != -1) //script2.js - Get a pretty background color onload
clientConn.Send(Encoding.ASCII.GetBytes("window.onload=function(){document.body.style.backgroundColor='#FF99FF';};"));
else if (reqHeader.IndexOf("iframe") != -1) //Noob iframe that just has text.
clientConn.Send(Encoding.ASCII.GetBytes("blargh zargh nargh dargh pikachu tangerine zombie destroy annihilate"));
else //hai dere is the body.innerHTML, load script1.js and script2.js
clientConn.Send(Encoding.ASCII.GetBytes("<html><head><script src='script1.js'></script><script src='script2.js'></script></head><body>mainPage<iframe src='iframe.html'>u no haz iframe</iframe></body></html>"));
clientConn.Close(); //Close the connection to client. We've done such a good job!
System.Console.WriteLine((System.DateTime.Now - startTime).TotalMilliseconds);
}
}
}
}
... И теперь я полностью сбит с толку, потому что вышеприведенная программа будет обслуживать страницу + скрипт + iframe в веб-браузере в течение 2 секунд [подключение от localhost к localhost, брандмауэр Windows + антивирус отключены]. При использовании такого сервера, как Apache, запросы [с использованием предварительно созданных файлов в файловой системе, разумеется] будут обрабатываться менее чем за 100 миллисекунд.
Из того, что я вижу, код, который я разместил, является чрезвычайно урезанным http-сервером. Производительность не меняется, если я отправляю заголовки ответа или нет.
Проблема становится чрезвычайно раздражающей, когда у меня есть проекты с 30+ файлами сценариев JavaScript, и для полной загрузки страницы требуется более 20 секунд [что недопустимо, когда все сценарии <10 КБ] </p>
Если вы посмотрите на скрипт, вы заметите, что я отслеживаю, когда сокет принимает и когда он закрывается, и обычно время обработки составляет всего 1-10 миллисекунд, что означает, что задержка между сервером http и веб браузер, [до того как соединение будет принято?]
Я в тупике и нуждаюсь в помощи.
Спасибо.
Другие примечания:
- «Настоящий» сервер, который я написал, принимает клиентские соединения и передает их другому потоку для выполнения работы, поэтому задача отправки байтов и закрытия соединения не является причиной такой большой задержки.
- Связывается с портом 80, чтобы проверить код, запустите программу и перейдите к http://127.0.0.1/ или http://localhost/
Изображения:
Инструменты разработчика Chrome, показывающие время загрузки
Дополнительная программа:
Я попытался смоделировать проблему, создав очень простую программу сервер / клиент ... Проблема НЕ была воспроизведена, и время соединения составляло 1 миллисекунду. Я еще тупее
using System;
using System.Text;
using System.Net;
using System.Net.Sockets;
using System.Threading;
namespace SlowSocketAccept
{
class Program
{
static DateTime connectionBeginTime;
static bool listening = false;
static void Main(string[] args)
{
new Thread(ClientThread).Start();
Socket listenerSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
listenerSocket.Bind(new IPEndPoint(IPAddress.Any, 80));
listenerSocket.Listen(80);
listening = true;
Socket newConn = listenerSocket.Accept();
byte[] reqHeader = new byte[1024];
newConn.Receive(reqHeader);
newConn.Send(Encoding.ASCII.GetBytes("Response Header\r\n\r\nContent"));
newConn.Close();
Console.WriteLine("Elapsed time: {0} ms", (DateTime.Now - connectionBeginTime).TotalMilliseconds);
}
static void ClientThread()
{
while (listening == false) ; //Busy wait, whatever it's an example
System.Threading.Thread.Sleep(10); //Wait for accept to be called =/
Socket s = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
connectionBeginTime = DateTime.Now;
s.Connect(new IPEndPoint(IPAddress.Parse("127.0.0.1"), 80));
s.Send(Encoding.ASCII.GetBytes("Request Header"));
byte[] response = new byte[1024];
s.Receive(response);
}
}
}
[a и b - темы]
а) Запустить тему 'b'
б) занят ожиданием, пока А не установит флаг, говоря, что все в порядке, чтобы подключиться
а) создать сокет, начать слушать 80, установить флаг, говорящий B, что все в порядке, чтобы подключиться
б) Создать сокет, сохранить текущее время и подключиться к 127.0.0.1:80 [localhost: 80]
а) поток принимает соединение и начинает слушать
б) поток отправляет фиктивную строку [наш заголовок запроса]
а) принимает фиктивную строку, затем отправляет фиктивную строку обратно [заголовок ответа + содержимое]
а) закрыть соединение
Истекшее время составляет около 1 миллисекунды = /