1

I have a console app that pings a website every few seconds. The loop works perfect on .NET Core. But when I try to run the same exact code on .NET Framework it runs through the loop twice and then stops. I can't figure out why it stops. Both .NETs have the latest versions installed, but the .NET Framework code stops on the third iteration and it stops on the HttpWebResponse response = (HttpWebResponse)request.GetResponse();,

while (!Console.KeyAvailable)
{
    try
    {
        Console.WriteLine("Pinging....");
        //WebRequest request = WebRequest.Create(args[1].ToString());
        WebRequest request = WebRequest.Create("https://www.google.com");
        request.Credentials = CredentialCache.DefaultCredentials;
        HttpWebResponse response = (HttpWebResponse)request.GetResponse();

        if ((int)response.StatusCode == 200)
        {
            // success code here
        }
        else if ((int)response.StatusCode != 200)
        {
            // successful connection but not code 200
        }

        Console.WriteLine(response.StatusDescription);
        Console.Write((int)response.StatusCode + "\n");
    }
    catch (WebException webexcp)
    {
        Console.ForegroundColor = ConsoleColor.Red;
        Console.WriteLine("Exception Caught");
        Console.WriteLine("Site not found");
        WebExceptionStatus status = webexcp.Status;
    }

    System.Threading.Thread.Sleep(500);
}
mjwills
  • 23,389
  • 6
  • 40
  • 63
StringKid
  • 13
  • 4
  • 3
    Have you used a debugger to find out why? – Dai Jul 29 '19 at 22:12
  • The obvious asumption would be that Console.KeyAvalible behaves differently in .NET Core and .NET Framework. Alternatively it could be that a exception is thrown somewhere inside the loop or a continue is called. You should propably remove that Thread.Sleep(500) near the end. If you want to wait to pause the console to read the output, use `Console.ReadKey();` without considering the return value. – Christopher Jul 29 '19 at 22:27
  • @Dai I used it and one of the solutions that came up was "using (httpwebresponse response =....)" and it wrapped the if and else if in {}. I just ran it and it works with that debug solution but I don't understand how that keeps the loop running. Doesn't look much different from the code I have now. – StringKid Jul 29 '19 at 22:27
  • 2
    @StringKid Now that you mention it, it seems you never dispose of the Network resources. Depending on how they are implemented, you could run out of resources in one of them but not the other. One advancement of .NET Core was that it is less strict about the need to dispose, due to more internal connection pooling. – Christopher Jul 29 '19 at 22:30
  • @Christopher So putting the webresponse with a using refreshes the resources? Doing this "using (HttpWebResponse response = (HttpWebResponse)request.GetResponse())" has been the only way that the loop has been able to continue. – StringKid Jul 29 '19 at 22:35
  • 1
    Note `We don't recommend that you use WebRequest or its derived classes for new development. Instead, use the System.Net.Http.HttpClient class.` taken from MSDN https://learn.microsoft.com/en-us/dotnet/api/system.net.webrequest?view=netframework-4.8 – garethb Jul 29 '19 at 23:57

2 Answers2

4

The problem is that .NET Framework limits a console app to two outgoing requests per domain.

Thus, you either need to increase the value of ServicePointManager.DefaultConnectionLimit or (preferably) Dispose of response when you are done with it.

By disposing, that request no longer counts towards the limit of two.

So why did this not impact .NET Core? Because it allows int.MaxValue, rather than 2, concurrent connections by default.

mjwills
  • 23,389
  • 6
  • 40
  • 63
  • Thank you that was the problem. I realize now that putting "using" with the response resets the connection. What I did was ServicePointManager.DefaultConnectionLimit = int.MaxValue; – StringKid Jul 30 '19 at 00:02
  • @StringKid `What I did was ServicePointManager.DefaultConnectionLimit = int.MaxValue;` I'd suggest 50 or 100 may be more appropriate values for most scenarios. And make sure you remember to use `using` / `Dispose`. – mjwills Jul 30 '19 at 00:04
0

I think the issue is that you're not disposing of the response object, so you're hitting the maximum number of allowed requests.

You can dispose of it in a using statement like:

Console.WriteLine("Pinging....");

WebRequest request = WebRequest.Create("https://www.google.com");
request.Credentials = CredentialCache.DefaultCredentials;

using (HttpWebResponse response = (HttpWebResponse)request.GetResponse())
{
    Console.WriteLine($"Status: {(int)response.StatusCode} {response.StatusDescription}");
}

You could instead use the HttpClient class for this instead, which doesn't have the same restriction (you don't have to dispose of the response):

static readonly HttpClient client = new HttpClient();

private static void Main()
{
    while (!Console.KeyAvailable)
    {
        PingWebsite("http://www.google.com/");
        Thread.Sleep(500);
    }
}

private static async void PingWebsite(string site)
{
    Console.WriteLine("Pinging...");

    try
    {
        HttpResponseMessage response = await client.GetAsync(site);
        response.EnsureSuccessStatusCode();
        Console.WriteLine($"Status: {(int) response.StatusCode} {response.ReasonPhrase}");
    }
    catch (HttpRequestException e)
    {
        Console.WriteLine($"\nException Caught!\nMessage: {e.Message}");
    }
}
Rufus L
  • 36,127
  • 5
  • 30
  • 43
  • Thank you, that was the problem. I guess that in .NET Core 2.2.6 there isn't a connection limit by default but .NET Framework 4.7.2 the limit is set to 2 – StringKid Jul 30 '19 at 00:03