【问题标题】:HttpClient connection state remains in TIME_WAIT after use for a while causing Socket ExceptionHttpClient 连接状态在使用一段时间后保持在 TIME_WAIT 导致 Socket Exception
【发布时间】:2019-05-04 14:03:27
【问题描述】:

我们正在使用HttpClient 访问托管在另一台服务器上的WebAPI。 我们有一个 MVC5 应用程序,它使用 IIS 8.5 从 WebAPI window server 2012 R2 获取数据

以下是用于从 webapi 获取数据的代码

//creating static instance of http client
private static HttpClient client = new HttpClient();

//static method to add common header
static ApiCall()
{
   client.DefaultRequestHeaders.Add("Authorization", 
                 string.Format("Value {0}", AppSettings.ApiSecurityKey));
   client.DefaultRequestHeaders.ConnectionClose = true;
}

//finally posting data to the api
//objprop contains the data and other setting
string url = "apiurl";
var postContent = new StringContent(objprop.DataForPost, 
      System.Text.Encoding.UTF8, objprop.ContentType);
response = client.PostAsync(url, postContent).Result;

但是在 PT 测试期间,我收到了多个处于 TIME_WAIT 状态的请求。因此,我们得到了套接字异常。

使用 HttpClient 的最佳实践是什么?

有没有什么设置可以在使用后释放socket。

类似的问题在这里
1.How do I prevent Socket/Port Exhaustion?
2.Why does HttpClient leave sockets open?
但这对我没有帮助。

编辑 1 更多细节

:InnerException- System.Net.Http.HttpRequestException:发送请求时出错。 ---> System.Net.WebException:无法连接到远程服务器 ---> System.Net.Sockets.SocketException:每个套接字地址(协议/网络地址/端口)通常只允许使用一次 x.x.x:80 在 System.Net.Sockets.Socket.InternalEndConnect(IAsyncResult asyncResult) 在 System.Net.Sockets.Socket.EndConnect(IAsyncResult asyncResult) 在 System.Net.ServicePoint.ConnectSocketInternal(布尔连接失败,套接字 s4,套接字 s6,套接字和套接字,IPAddress 和地址,ConnectSocketState 状态,IAsyncResult asyncResult,异常和异常) --- 内部异常堆栈跟踪结束 --- 在 System.Net.HttpWebRequest.EndGetRequestStream(IAsyncResult asyncResult,TransportContext& 上下文) 在 System.Net.Http.HttpClientHandler.GetRequestStreamCallback(IAsyncResult ar) --- 内部异常堆栈跟踪结束 --- : URL- http://x.x.x.com//api/GetMenu

【问题讨论】:

  • @शेखर 该链接如何提倡 HttpClient 的多个实例?这是因为您正在使用 .Result 阻止异步代码,并且您可能正在等待对该代码的调用,尽管您尚未在上下文中显示它。
  • @Crowcoder 我确实浏览了整个代码,对于评论感到抱歉。我正在调查。
  • @Crowcoder 你认为这是导致 TIME_WAIT 状态的原因吗?
  • 我不是网络工程师,但如果你没有死锁,那么我不会担心它,尽管你应该重构为不需要因其他原因阻塞代码。 HttpClient 将重用连接,这是首先使用共享静态实例的原因,所以我相信这是设计使然。

标签: c# asp.net-mvc websocket asp.net-web-api2 dotnet-httpclient


【解决方案1】:

您对HttpClient 的用法是正确的;尽管正如一些人所评论的那样,最好一直使用async,但这不是您错误的原因。

TIME_WAIT 是 TCP/IP 连接的强制阶段;一旦建立连接,发起连接关闭的一方将进入此状态一段预先配置的时间。

TCP/IP 有一个有限的端口池用于进出连接。

新连接使用其中的一个子集(也称为动态或临时端口范围)在每一侧分配一个端口。

您的应用在尝试创建新的传出连接时出现错误“正在使用的地址”,但 TCP 已用完动态范围内的本地端口。

根据启动连接关闭的一端,可能是您的客户端或服务器端口耗尽。

This article from Microsoft 详细介绍了如何修改 Windows TCP/IP 设置以实现高连接率:

  • 将 TIME_WAIT 周期从 120 秒更改为 30 秒
  • 将动态端口数从 16384 增加到 64511

同时执行这两项操作会将最大连接速率从每秒 136 个连接增加到大约 2150 个。

【讨论】:

    【解决方案2】:

    经过多次检查,使用wireshark 和telnet -n 5,我发现了一种有效的方法来降低httpClient 的套接字使用率是编写我自己的POST 套接字方法。

        private void PostSocket(MyObject o)
        {
            string host = "localhost";
            int port = 9876;
            
            IPAddress ipAddress = IPAddress.Parse("127.0.0.1");
            IPEndPoint remoteEP = new IPEndPoint(ipAddress, port);
    
            // Create a TCP socket
            Socket sock = new Socket(ipAddress.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
            sock.Connect(remoteEP);
    
            // The ToJson uses Newtonsoft method
            var data = new StringContent(o.ToJson(o), Encoding.UTF8, "application/json");
            byte[] byteData = data.ReadAsByteArrayAsync().Result;
    
            var post = $"POST /prices HTTP/1.1" + Environment.NewLine;
            post += "Content-Type: application/json" + Environment.NewLine;
            post += "Host: " + host + ":" + port + Environment.NewLine;
            post += "Content-Length: " + byteData.Length + Environment.NewLine;
            // This extra newline is required
            post += Environment.NewLine;
            byte[] postData = Encoding.UTF8.GetBytes(post);
    
            byte[] sendData = new byte[byteData.Length + postData.Length];
            Array.Copy(postData, 0, sendData, 0, postData.Length);
            Array.Copy(byteData, 0, sendData, postData.Length, byteData.Length);
    
            // Minimum socket send timeout
            sock.SendTimeout = 500;
            sock.Send(sendData);
            
            // If this is not set to false it hangs
            sock.Disconnect(false);
        }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2020-11-07
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多