问题概述:一段时间以来,我一直在尝试编写自定义 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 浏览器提供页面+脚本+iframe [从本地主机连接到本地主机,Windows 防火墙+防病毒软件已关闭]。使用像 Apache 这样的服务器,请求 [当然是在文件系统中使用预先创建的文件] 肯定会在不到 100 毫秒的时间内得到处理。
据我所知,我发布的代码是一个极其精简的 http 服务器。无论是否发送响应 header ,性能都不会改变。
当我的项目包含 30 多个 JavaScript 脚本文件时,问题变得非常烦人,需要 20 多秒才能完全加载页面 [当脚本都是 <10 kb 时这是 Not Acceptable ]
如果您查看脚本,您会注意到我跟踪套接字何时接受以及何时关闭,通常处理时间仅为 1-10 毫秒,这意味着延迟存在于 http 服务器和 web 之间浏览器,[在接受连接之前?]
我很困惑,需要帮助。 谢谢。
其他说明: - 我编写的“真实”服务器接受客户端连接并将它们传递给另一个线程来完成工作,因此发送字节和关闭连接的任务并不是造成如此多延迟的原因。 - 绑定(bind)到端口 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 [localhost:80]
a) 线程接受连接并开始监听
b) 线程发送一个虚拟字符串[我们的请求头]
a) 接受虚拟字符串,然后发回一个虚拟字符串 [响应头 + 内容]
a) 关闭连接
耗时大约是1毫秒=/
最佳答案
除非您包含一个 Content-Length header ——并指定您使用的是 HTTP 1.0,或者连接应该关闭——浏览器会一直读取内容,直到它注意到连接已关闭(这可能需要而)。
确保您的应用正在发送 Content-Length header 和“连接:关闭”。
此外,您应该在关闭客户端套接字之前将其关闭。这告诉 .net(也许还有其他方面,我忘记了)您已完成发送数据,并且应该将其刷新到网络并重置连接。 Close
是代码方面的事情; Shutdown
从客户端的 Angular 来看实际上是关闭连接。
关于c# - 浏览器尝试连接和 Socket.Accept() 之间的长时间延迟 [~1s],我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/3476404/