使用一段时间后,HttpClient连接状态仍停留在TIME_WAIT状态,导致套接字异常。

6

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

以下是用于从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的最佳实践是什么?

是否有任何设置可以在使用后释放套接字?

类似的问题在这里
1. 如何防止套接字/端口耗尽?
2. 为什么HttpClient会保持套接字开启?
但是它没有帮助我。

编辑1 更多细节

: InnerException- System.Net.Http.HttpRequestException: 发送请求时发生错误。---> System.Net.WebException:无法连接到远程服务器---> System.Net.Sockets.SocketException:协议/网络地址/端口只允许每个套接字地址 (protocol/network address/port) 通常是被允许 x.x.x:80 at System.Net.Sockets.Socket.InternalEndConnect(IAsyncResult asyncResult) at System.Net.Sockets.Socket.EndConnect(IAsyncResult asyncResult) at System.Net.ServicePoint.ConnectSocketInternal(Boolean connectFailure, Socket s4, Socket s6, Socket& socket, IPAddress& address, ConnectSocketState state, IAsyncResult asyncResult, Exception& exception) --- 内部异常堆栈跟踪的结尾 --- at System.Net.HttpWebRequest.EndGetRequestStream(IAsyncResult asyncResult, TransportContext& context) at System.Net.Http.HttpClientHandler.GetRequestStreamCallback(IAsyncResult ar) --- 内部异常堆栈跟踪的结尾 --- : URL- http://x.x.x.com//api/GetMenu


1
@शेखर 这个链接是如何支持多个 HttpClient 实例的呢?它是关于你使用了 .Result 阻塞异步代码的事实,而且你可能正在等待这段代码的调用,尽管你没有在上下文中展示它。 - Crowcoder
@Crowcoder,我已经仔细查看了整个代码,抱歉之前的评论。我正在研究这个问题。 - शेखर
我并不是一名网络工程师,但如果你没有发生死锁,那么我就不会担心它,不过,出于其他原因,你应该重构以不需要阻塞代码。HttpClient将重用连接,这是使用共享静态实例的原因之一,因此我认为这是设计上的考虑。 - Crowcoder
默认配置中套接字存在限制,我已经达到了那个阈值。在此期间,我们的负载均衡器也存在套接字问题。 - शेखर
请在下投票的原因中注明。 - शेखर
显示剩余8条评论
2个回答

4
您使用HttpClient是正确的;虽然一些人已经评论过最好使用async,但这并不是您错误的原因。 TIME_WAIT是TCP/IP连接的必要阶段;一旦建立连接,发起连接关闭的一方将在此状态下停留预设时间。
TCP/IP对于连接进出有一个有限的端口池。
新连接使用其中的一个子集(称为动态或短暂端口范围)在每一侧分配一个端口。
当尝试创建新的传出连接,但TCP已经用完动态范围内的本地端口时,您的应用程序会收到错误“地址正在使用”。 根据哪一端开始连接关闭,可能是您的客户端或服务器用尽了端口。 这篇来自Microsoft的文章详细介绍了如何修改Windows TCP/IP设置以提高连接速率:
  • TIME_WAIT周期从120秒改为30秒
  • 将动态端口计数增加从16384到64511
执行这两步操作将将最大连接速率从136个连接/秒提高到约2150个连接/秒。

0

经过多次检查,使用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);
    }

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接