代码之家  ›  专栏  ›  技术社区  ›  Warty

浏览器尝试连接和套接字之间的长延迟[~1s]。接受()

  •  3
  • Warty  · 技术社区  · 14 年前

    问题概述:我已经开始尝试编写自定义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);
                }
            }
        }
    }
    

    …我现在完全困惑了,因为上面的程序将在2秒钟的时间跨度内为Web浏览器提供page+script+iframe服务[从本地主机连接到本地主机,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是螺纹]
    a)启动螺纹“B”
    b)忙碌等待,直到A设置一个标志表示可以连接
    a)创建套接字,开始监听80,设置一个标志告诉b可以连接
    b)创建套接字,存储当前时间,并连接到127.0.0.1:80[本地主机:80]
    a)线程接受连接并开始侦听
    b)线程发送一个虚拟字符串[我们的请求头]
    a)接受虚拟字符串,然后将虚拟字符串发回[响应头+内容]
    a)紧密连接

    经过的时间大约是1毫秒=/

    1 回复  |  直到 14 年前
        1
  •  2
  •   cHao Hammerite    14 年前

    除非您包含一个Content-Length头——并指定您正在使用HTTP 1.0,或者连接应该关闭——否则浏览器将一直读取内容,直到它注意到连接已关闭(这可能需要一段时间)。

    确保你的应用程序正在发送一个Content-Length标题和“connection:close”。

    另外,在关闭客户机套接字之前应该先关闭它。这会告诉.NET(也许是另一方,我忘记了)您已经完成了数据发送,应该将其刷新到网络并重置连接。 Close 是代码方面的事情; Shutdown 是从客户机的角度关闭连接的真正原因。