简体   繁体   English

C#HttpClient - 我可以强制关闭连接吗?

[英]C# HttpClient - can I force close the connection?

We are seeing an issue where a customer is hitting our API and cancelling the request before it completes. 我们遇到的问题是客户在使用API​​并在完成之前取消请求。 We believe that they are also closing the connection so the requests are not getting written to the IIS logs. 我们认为他们也在关闭连接,因此请求不会写入IIS日志。 I'm trying to create a runner to duplicate this behavior. 我正在尝试创建一个运行器来复制此行为。 What I can't figure out is how can I force close the connection so that the server cannot send data back? 我无法弄清楚的是如何强制关闭连接以便服务器无法发回数据?

Here is my current code: 这是我目前的代码:

 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();
            }
        }
    }
}

The code correctly cancels the request after 5 seconds but IIS still seem to send back data after the API call completes (set to 10 seconds). 代码在5秒后正确取消请求,但IIS似乎仍在API调用完成后发送回数据(设置为10秒)。

How can I close the connection so that IIS cannot send back any data after my 5s timeout? 如何关闭连接,以便IIS在5秒超时后无法发回任何数据?

I was finally able to duplicate the problem by using raw sockets, which is a lot of work. 我终于能够通过使用原始套接字来复制问题,这是很多工作。 The way to get IIS to log the cancelled requests is to do the following in the code below: 让IIS记录已取消的请求的方法是在下面的代码中执行以下操作:

  1. do NOT send Connection: Close 不要发送Connection: Close
  2. do NOT call Socket.Dispose() 不要调用Socket.Dispose()

Here is the final working code, hopefully someone else will find this useful: 这是最终的工作代码,希望其他人会发现这很有用:

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();
            }
        }

You are probably hit by a very low timeout setting. 您可能会遇到非常低的超时设置。 If you set it to a sufficiently short amount of time, the client will close the connection although the server still tries to return data. 如果将其设置为足够短的时间,客户端将关闭连接,尽管服务器仍尝试返回数据。

If this doesn't reproduce the problem you could also look into concurrency and the property 'UseNagleAlgorithm' on the servicepoint manager. 如果这不能重现问题,您还可以在服务点管理器上查看并发性和属性“UseNagleAlgorithm”。 This property being true attempts to pigiback several small http requests in the same TCP package. 此属性为true,尝试在同一TCP包中对几个小的http请求进行pigiback。 It is a band with saver, but causes client timeouts as the os delays sending data through the socket until it has gathered enough data. 它是一个带有保护程序的乐队,但会导致客户端超时,因为操作系统延迟通过套接字发送数据,直到它收集了足够的数据。

Have you tried calling Dispose on the response object instead? 您是否尝试过在响应对象上调用Dispose?

Check out this post: Force connection to close with HttpClient in WCF 看看这篇文章: 在WCF中强制连接关闭HttpClient

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM