2010-08-13 37 views
3

问题概述:我一直在玩编写自定义http服务器应用程序一段时间。我发现,当任何连接到我的服务器应用程序的网络浏览器,将有一个0.5-1秒的“延迟”(根据谷歌浏览器),在处理请求之前[这将需要几毫秒]浏览器尝试连接和Socket.Accept之间的延迟[〜1s]()

我最终尝试做一个虚拟程序来查明问题:

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秒的时间跨度[从本地连接到本地主机,Windows防火墙+防病毒关闭]。使用像Apache这样的服务器,[当然在文件系统中使用预先创建的文件]的请求肯定会在不到100毫秒的时间内处理。

从我所看到的,我发布的代码是一个非常简洁的http服务器。如果我发送响应标题,性能不会改变。

问题就变得时,我有30 + JavaScript的脚本文件的项目非常恼人,以20+秒的页面完全加载[当脚本这是不能接受的所有< 10 KB]

如果你看看在脚本中,您会注意到我跟踪的是套接字何时接收以及何时关闭,并且通常处理时间仅为1-10毫秒,这意味着http服务器和Web浏览器之间的延迟[在连接之前被接受?]

我很难过,需要帮助。 谢谢。

其他说明: - 我写的“真实”服务器接受客户端连接并将它们传递给另一个线程来完成这项工作,因此发送字节和关闭连接的任务并不是这么简单的原因落后。 - 绑定到端口80,测试代码运行程序并导航到http://127.0.0.1/http://localhost/

图片:
Chrome's developer tools showing the load times


附加程序:
我试图通过创建一个模拟问题非常简单的服务器/客户端程序...该问题未被重现,连接时间为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'
B)忙等到设置一个标志说这没关系连接
一)创建套接字,开始听80,设置一个标志告诉B可以连接
b)创建套接字,存储当前时间,并连接到127.0.0。a)线程接受连接并开始监听
b)线程发送一个虚拟字符串[我们的请求头文件]
a)接受虚拟字符串,然后发送一个虚拟字符串返回[响应头文件+的含量]
a)紧靠连接

经过时间为约1毫秒=/

回答

2

除非你包括Content-Length头 - 并指定您使用HTTP 1.0,或该连接应该关闭 - 浏览器会继续阅读内容,直到它注意到连接已关闭(可能需要一段时间)。

确保您的应用正在发送内容长度标题,并且“连接:关闭”。

另外,您应该在关闭之前关闭客户端套接字。这告诉.net(也许还有其他方面,我忘了)你已经完成了数据发送,并且它应该被刷新到网络并且连接重置。 Close是代码方面的东西; Shutdown实际上是从客户的角度关闭连接。

+0

如果我转到127.0.0.1,该解决方案会将我的加载时间缩短很多[从30秒到1秒]。但是,如果我导航到本地主机,而加载时间仍然是20+秒,这对我来说没有意义,因为根据主机文件localhost == 127.0.0.1。任何想法可能会发生什么?防病毒功能已禁用。如果我转到192.168.0.194 [我的网络上的我的IP],那么加载时间也是1秒,而不是30秒。 – Warty 2010-08-13 22:10:29

+0

总猜在这里,但你看看头?如果添加“Host:localhost”标头会导致标头大小超过1024字节(因为您没有检查标题,只能读取1024字节),这可能会导致问题 - 服务器可能会在之前开始发送浏览器预计会收到任何东西。这在本地主机上尤其明显,因为几乎没有延迟。 – cHao 2010-08-13 22:48:20

+0

顺便说一句,你的代码真的应该看标题,并确保你有一个有效的请求。浏览器通常会做正确的事情,但请参阅上文。而少咸味的人物会找到一些方法来利用松懈的检查。 – cHao 2010-08-13 22:53:55