C# HttpClient - 我可以强制关闭连接吗?
C# HttpClient - can I force close the connection?
我们发现一个问题,即客户点击我们的 API 并在请求完成前取消请求。我们相信他们也在关闭连接,因此请求不会写入 IIS 日志。我正在尝试创建一个跑步者来复制这种行为。我想不通的是 如何强制关闭连接,使服务器无法发回数据?
这是我当前的代码:
private static async Task RunMe()
{
var cts = new CancellationTokenSource();
cts.CancelAfter(5000);
using (var client = new HttpClient())
{
try
{
client.DefaultRequestHeaders.ConnectionClose = true;
client.DefaultRequestHeaders.Add("x-apikey", "some key");
Console.WriteLine("making request");
using (var response = await client.GetAsync("https://foo.com/api/apitest/testcancel", cts.Token))
{
var result = await response.Content.ReadAsStringAsync();
Console.WriteLine($"Request completed: {result}");
}
}
finally
{
client.CancelPendingRequests();
Console.WriteLine("about to dispose the client");
client.Dispose();
}
}
}
}
代码在 5 秒后正确取消了请求,但 IIS 似乎在 API 调用完成(设置为 10 秒)后仍发回数据。
如何关闭连接,使 IIS 在 5 秒超时后无法发回任何数据?
您是否尝试过在响应对象上调用 Dispose?
看看这个 post:
您可能遇到了超时设置过低的问题。如果您将其设置为足够短的时间,客户端将关闭ose 连接,尽管服务器仍会尝试return 数据。
如果这不能重现问题,您还可以查看服务点管理器上的并发性和 属性 'UseNagleAlgorithm'。 属性 为真会尝试在同一个 TCP 包中搭载几个小的 http 请求。它是一个带保护器的带,但会导致客户端超时,因为 os 延迟通过套接字发送数据,直到它收集到足够的数据。
我终于能够通过使用原始套接字重现该问题,这需要大量工作。让 IIS 记录取消的请求的方法是在下面的代码中执行以下操作:
- 不要发送
Connection: Close
- 请勿致电
Socket.Dispose()
这是最终的工作代码,希望其他人会发现它有用:
private static void SocketTime(int readTimeout, Random random)
{
var ip = IPAddress.Parse("127.0.0.1");
const int hostPort = 80;
var hostep = new IPEndPoint(ip, hostPort);
var sock = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
sock.Connect(hostep);
var uniqueID = random.Next().ToString();
var get = $"GET http://localhost/api/apitest/testcancel/{uniqueID} HTTP/1.1\r\n" +
"x-apikey: somekey\r\n" +
"Host: foo.com\r\n" +
"Connection: Close\r\n" +
"\r\n";
var getBytes = Encoding.UTF8.GetBytes(get);
sock.ReceiveTimeout = readTimeout;
sock.Send(getBytes);
var responseData = new byte[1024];
try
{
var bytesRead = sock.Receive(responseData);
var responseString = new StringBuilder();
while (bytesRead != 0)
{
responseString.Append(Encoding.ASCII.GetChars(responseData), 0, bytesRead);
bytesRead = sock.Receive(responseData);
}
Console.WriteLine($"SUCCESS => Unique ID = {uniqueID}, Timeout = {readTimeout}");
}
catch (SocketException e)
{
if (e.SocketErrorCode == SocketError.TimedOut)
{
Console.WriteLine($"FAIL => Unique ID = {uniqueID}, Timeout = {readTimeout}");
}
else
{
Console.WriteLine("UNHNALDED ERROR: " + e.Message);
}
}
finally
{
sock.Dispose();
}
}
我们发现一个问题,即客户点击我们的 API 并在请求完成前取消请求。我们相信他们也在关闭连接,因此请求不会写入 IIS 日志。我正在尝试创建一个跑步者来复制这种行为。我想不通的是 如何强制关闭连接,使服务器无法发回数据?
这是我当前的代码:
private static async Task RunMe()
{
var cts = new CancellationTokenSource();
cts.CancelAfter(5000);
using (var client = new HttpClient())
{
try
{
client.DefaultRequestHeaders.ConnectionClose = true;
client.DefaultRequestHeaders.Add("x-apikey", "some key");
Console.WriteLine("making request");
using (var response = await client.GetAsync("https://foo.com/api/apitest/testcancel", cts.Token))
{
var result = await response.Content.ReadAsStringAsync();
Console.WriteLine($"Request completed: {result}");
}
}
finally
{
client.CancelPendingRequests();
Console.WriteLine("about to dispose the client");
client.Dispose();
}
}
}
}
代码在 5 秒后正确取消了请求,但 IIS 似乎在 API 调用完成(设置为 10 秒)后仍发回数据。
如何关闭连接,使 IIS 在 5 秒超时后无法发回任何数据?
您是否尝试过在响应对象上调用 Dispose?
看看这个 post:
您可能遇到了超时设置过低的问题。如果您将其设置为足够短的时间,客户端将关闭ose 连接,尽管服务器仍会尝试return 数据。
如果这不能重现问题,您还可以查看服务点管理器上的并发性和 属性 'UseNagleAlgorithm'。 属性 为真会尝试在同一个 TCP 包中搭载几个小的 http 请求。它是一个带保护器的带,但会导致客户端超时,因为 os 延迟通过套接字发送数据,直到它收集到足够的数据。
我终于能够通过使用原始套接字重现该问题,这需要大量工作。让 IIS 记录取消的请求的方法是在下面的代码中执行以下操作:
- 不要发送
Connection: Close
- 请勿致电
Socket.Dispose()
这是最终的工作代码,希望其他人会发现它有用:
private static void SocketTime(int readTimeout, Random random)
{
var ip = IPAddress.Parse("127.0.0.1");
const int hostPort = 80;
var hostep = new IPEndPoint(ip, hostPort);
var sock = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
sock.Connect(hostep);
var uniqueID = random.Next().ToString();
var get = $"GET http://localhost/api/apitest/testcancel/{uniqueID} HTTP/1.1\r\n" +
"x-apikey: somekey\r\n" +
"Host: foo.com\r\n" +
"Connection: Close\r\n" +
"\r\n";
var getBytes = Encoding.UTF8.GetBytes(get);
sock.ReceiveTimeout = readTimeout;
sock.Send(getBytes);
var responseData = new byte[1024];
try
{
var bytesRead = sock.Receive(responseData);
var responseString = new StringBuilder();
while (bytesRead != 0)
{
responseString.Append(Encoding.ASCII.GetChars(responseData), 0, bytesRead);
bytesRead = sock.Receive(responseData);
}
Console.WriteLine($"SUCCESS => Unique ID = {uniqueID}, Timeout = {readTimeout}");
}
catch (SocketException e)
{
if (e.SocketErrorCode == SocketError.TimedOut)
{
Console.WriteLine($"FAIL => Unique ID = {uniqueID}, Timeout = {readTimeout}");
}
else
{
Console.WriteLine("UNHNALDED ERROR: " + e.Message);
}
}
finally
{
sock.Dispose();
}
}