6

We are using HttpClient to access the WebAPI hosted on another server. We have a MVC5 application which is getting data form WebAPI window server 2012 R2 with IIS 8.5

Below is the code which using to get data from 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;

But during the PT test I am getting multiple request in TIME_WAIT state. Due to this we are getting socket exception.

What are the best practices to use HttpClient?

Is there any setting which can release the socket after the use.

Similar Question is here
1. How do I prevent Socket/Port Exhaustion?
2. Why does HttpClient leave sockets open?
but it did not helped me.

Edit 1 More detail

: InnerException- System.Net.Http.HttpRequestException: An error occurred while sending the request. ---> System.Net.WebException: Unable to connect to the remote server ---> System.Net.Sockets.SocketException: Only one usage of each socket address (protocol/network address/port) is normally permitted 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) --- End of inner exception stack trace --- at System.Net.HttpWebRequest.EndGetRequestStream(IAsyncResult asyncResult, TransportContext& context) at System.Net.Http.HttpClientHandler.GetRequestStreamCallback(IAsyncResult ar) --- End of inner exception stack trace --- : URL- http://x.x.x.com//api/GetMenu

शेखर
  • 17,412
  • 13
  • 61
  • 117
  • https://blog.stephencleary.com/2012/07/dont-block-on-async-code.html – GSerg May 04 '19 at 14:07
  • 1
    @शेखर how does that link advocate multiple instances of HttpClient? It is about the fact that you are blocking the async code with `.Result` and you are probably awaiting the call to this code though you haven't shown it in context. – Crowcoder May 04 '19 at 14:13
  • @Crowcoder I did went through the whole code sorry for the comment. I am looking into that. – शेखर May 04 '19 at 14:14
  • @Crowcoder do you think this is causing the state in TIME_WAIT? – शेखर May 04 '19 at 14:29
  • I'm not a network engineer but if you are not deadlocking then I wouldn't worry about it though you should refactor to not need blocking code for other reasons. HttpClient will re-use connections, that is a reason to use a shared static instance in the first place, so I believe it is by design. – Crowcoder May 04 '19 at 15:35
  • there is limitation of socket in default configuration I am reaching to that threashhold value. In between we have load balancer on which too there are issue of socket – शेखर May 04 '19 at 18:59
  • Are you dispose HttpClient object ? – LuisEduardox May 06 '19 at 15:28
  • @LuisEduardox no because i have a static member and I want to reuse them – शेखर May 06 '19 at 15:28
  • I understand that you using a static HttpClient to avoid multiple instances/sockets to endpoint, but its sound like a concurrency issue. Maybe you could use IRestClient [check](http://www.nimaara.com/2016/11/01/beware-of-the-net-httpclient/), I like more IRestClient instead HttpClient when have concurrency a lot. – LuisEduardox May 06 '19 at 15:41
  • Put the reason for down vote – शेखर May 07 '19 at 02:21
  • Is there any specific reason for `Connection: close`? https://serverfault.com/a/639547 – cem May 10 '19 at 00:37
  • You have quoted snippets from your code but not context. The bug could easily be in the unquoted code that shows how the snippets are used. Can you provide a self-contained code block that repros the issue? – David Wright May 10 '19 at 23:42
  • The issue was solved by adding some registry value on the server which increase the number of sockets. – शेखर May 11 '19 at 06:49

2 Answers2

4

Your usage of HttpClient is correct; although its better to use async all the way up as some have commented, its not the cause of your error.

TIME_WAIT is a mandatory phase of a TCP/IP connection; once a connection is established, the side that initiates connection close will go into this state for a preconfigured time.

TCP/IP has a limited pool of ports for connections in and out.

New connections use a subset of these (aka dynamic or ephemeral port range) to allocate a port on each side.

Your app gets error "Address in use" when trying to create a new outgoing connection, but TCP has run out of local ports in the dynamic range.

Depending on which end initiates connection close, it may be your client or server which runs out of ports.

This article from Microsoft details how to modify Windows TCP/IP settings for high connection rates:

  • Change TIME_WAIT period from 120 to 30 seconds
  • Increase the dynamic port count from 16384 to 64511

Doing both of these will increase the max connection rate from 136 connections/second to about 2150.

Peter Wishart
  • 11,600
  • 1
  • 26
  • 45
0

After much checking around, using wireshark and telnet -n 5 I found an efficient way to keep socket use down from httpClient was to write my own POST socket method.

    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);
    }
rupweb
  • 3,052
  • 1
  • 30
  • 57