4

我正在使用 HttpWebRequest 连接到我内部构建的 HTTP 服务器。我的问题是它比通过例如 PostMan ( https://chrome.google.com/webstore/detail/postman-rest-client/fdmmgilgnpjigdojojpjoooidkmcomcm?hl=en ) 连接到服务器要慢得多,这可能正在使用Chrome 中用于请求数据的内置函数。

服务器是使用 MSDN ( http://msdn.microsoft.com/en-us/library/dxkwh6zw.aspx ) 上的这个示例构建的,并使用 64 的缓冲区大小。请求是一个 HTTP 请求,正文中有一些数据.

通过 PostMan 连接时,请求被拆分成一堆块,并多次调用 BeginRecieve(),每次接收 64B,耗时约 2 毫秒。除了最后一个,它接收不到 64B。

但是当使用 HttpWebRequest 与我的客户端连接时,第一个 BeginRecieve() 回调接收到 64B 大约需要 1 毫秒,以下仅接收 47B 并且需要将近 200 毫秒,最后第三个接收大约 58B 并且需要 2 毫秒。

第二个 BeginRecieve 是怎么回事?我注意到,一旦我开始将数据写入 HttpWebRequest 输入流,连接就建立了,但是直到我调用 GetResponse() 才开始接收数据。

这是我的 HttpWebRequest 代码:

var request = (HttpWebRequest)WebRequest.Create(url);

request.Method = verb;
request.Timeout = timeout;
request.Proxy = null;
request.KeepAlive = false;
request.Headers.Add("Content-Encoding", "UTF-8");
System.Net.ServicePointManager.Expect100Continue = false;
request.ServicePoint.Expect100Continue = false;

if ((verb == "POST" || verb == "PUT") && !String.IsNullOrEmpty(data))
{
    var dataBytes = Encoding.UTF8.GetBytes(data);

    try
    {
        var dataStream = request.GetRequestStream();
        dataStream.Write(dataBytes, 0, dataBytes.Length);
        dataStream.Close();
    }
    catch (Exception ex)
    {
        throw;
    }

}

WebResponse response = null;
try
{
    response = request.GetResponse();
}
catch (Exception ex)
{
    throw;
}

var responseReader = new StreamReader(rStream, Encoding.UTF8);
var responseStr = responseReader.ReadToEnd();

responseReader.Close();
response.Close();

我究竟做错了什么?为什么它的行为与来自 Web 浏览器的 HTTP 请求如此不同?这实际上给我的应用程序增加了 200 毫秒的延迟。

4

4 回答 4

4

这看起来像是Nagle 算法与TCP 延迟确认发生冲突的典型案例。在您的情况下,您正在发送一个小的 Http 请求(根据您的数字,大约 170 个字节)。这可能小于 MSS(最大段大小),这意味着 Nagle 算法将启动。服务器可能会延迟 ACK,从而导致最多 500 毫秒的延迟。有关详细信息,请参阅链接。

ServicePointManager.UseNagleAlgorithm = false您可以通过(在发出第一个请求之前)禁用 Nagle ,请参阅MSDN

另请参阅Nagle 的算法对小请求不友好以进行详细讨论,包括 Wireshark 分析。

注意:在您的回答中,当您进行写-写-读时,您会遇到同样的情况。当你切换到读写模式时,你就克服了这个问题。但是,我不相信您可以指示 HttpWebRequest (或 HttpClient )将小请求作为单个 TCP 写操作发送。在某些情况下,这可能是一个很好的优化。尽管它可能会导致一些额外的数组复制,从而对性能产生负面影响。

于 2013-10-28T19:14:27.823 回答
3

200ms 是 Nagle 算法的典型延迟。这引起了服务器或客户端正在使用 Nagling 的怀疑。你说你正在使用 MSDN 中的一个示例作为服务器......好吧,你去吧。使用适当的服务器或禁用 Nagling。

假设内置 HttpWebRequest 类有不必要的 200 毫秒延迟是不太可能的。看看别处。查看您的代码以找出问题所在。

于 2013-08-27T15:58:16.790 回答
2

看起来 HttpWebRequest 真的很慢。

有趣的是:我使用 Sockets 实现了我自己的 HTTP 客户端,我发现了为什么 HttpWebRequest 这么慢的线索。如果我将我的 ASCII 标头编码为它自己的字节数组并将它们发送到流上,然后是从我的数据编码的字节数组,我的基于套接字的 HTTP 客户端的行为与 HttpWebRequest 完全相同:首先它用数据填充一个缓冲区(部分头),然后它部分使用另一个缓冲区(头的其余部分),等待 200 毫秒,然后发送其余数据。

编码:

TcpClient client = new TcpClient(server, port);
NetworkStream stream = client.GetStream();

// Send this out
stream.Write(headerData, 0, headerData.Length);
stream.Write(bodyData, 0, bodyData.Length);
stream.Flush();

解决方案当然是在将两个字节数组发送到流上之前附加它们。我的应用程序现在的行为符合预期。

具有单个流的代码写入:

TcpClient client = new TcpClient(server, port);
NetworkStream stream = client.GetStream();

var totalData = new byte[headerBytes.Length + bodyData.Length];
Array.Copy(headerBytes,totalData,headerBytes.Length);
Array.Copy(bodyData,0,totalData,headerBytes.Length,bodyData.Length);

// Send this out
stream.Write(totalData, 0, totalData.Length);
stream.Flush();

并且 HttpWebRequest 似乎在我写入请求流之前发送标头,因此它的实现可能有点像我的第一个代码示例。这有意义吗?

希望这对任何有同样问题的人有帮助!

于 2013-08-27T15:07:18.580 回答
-1

试试这个:你需要处理你的 IDisposables:

var request = (HttpWebRequest)WebRequest.Create(url);

request.Method = verb;
request.Timeout = timeout;
request.Proxy = null;
request.KeepAlive = false;
request.Headers.Add("Content-Encoding", "UTF-8");
System.Net.ServicePointManager.Expect100Continue = false;
request.ServicePoint.Expect100Continue = false;

if ((verb == "POST" || verb == "PUT") && !String.IsNullOrEmpty(data))
{
    var dataBytes = Encoding.UTF8.GetBytes(data);

    using (var dataStream = request.GetRequestStream())
    {
        dataStream.Write(dataBytes, 0, dataBytes.Length);
    }
}

string responseStr;
using (var response = request.GetResponse())
{
    using (var responseReader = new StreamReader(rStream, Encoding.UTF8))
    {
        responseStr = responseReader.ReadToEnd();
    }
}
于 2013-08-27T15:26:59.810 回答