Prevent “underlying connection was closed” errors in C#


[underlying connection was closed]

This example shows how to fix the error “The underlying connection was closed” when downloading a response from a web page.

Background

The basic approach for downloading a result from a web page is simple. Create a new WebClient object and use its DownloadString to download a URL result as a string. The following code snippet shows this technique.

using (WebClient client = new WebClient())
{
    return client.DownloadString(url);
}

This works fine for some web sites, but for others you get one of the following error messages.


[underlying connection was closed]

This seems to be caused by the web site using one of the newer security protocols. The solution is to set the ServicePointManager class’s static SecurityProtocol property to an appropriate value.

In older versions of .NET, that enumeration has two values, Ssl3 (Secure Socket Layer 3.0) and Tls (Transport Layer Security 1.0). Newer versions of the enumeration also include the values Tls11 (TLS 1.1), Tls12 (TLS 1.2), and Tls13 (TLS 1.3).

In Visual Studio 2019 with .NET Framework 4.5, I managed to get the earlier code snippet to work by setting SecurityProtocol to SecurityProtocolType.SystemDefault. That value has the numeric value 0, but for some reason setting this property to 0 doesn’t seem to work in Visual Studio 2008. Presumably that’s because of some difference in the Framework versions.

Fortunately explicitly setting the value to allow the TLS 1.1 and TLS 1.2 protocols seems to work.

A Fake Enumeration

To avoid using magic numbers, I wanted to create an enumeration holding the possible protocol values. Unfortunately the SecurityProtocol property expects a value of type SecurityProtocolType. If you set it equal to an enumeration that you define, Visual Studio complains that it cannot convert your enumeration into the necessary type.

In C# you can specify an enumeration’s type, but it must be an integral type such as int, uint, or short, so you cannot make it have the type SecurityProtocolType.

To work around that, I created the following class to define the possible values.

public class Protocols
{
    public const SecurityProtocolType
        protocol_SystemDefault = 0,
        protocol_Ssl3 = (SecurityProtocolType)48,
        protocol_Tls = (SecurityProtocolType)192,
        protocol_Tls11 = (SecurityProtocolType)768,
        protocol_Tls12 = (SecurityProtocolType)3072;
}

This class simply defines the protocol values listed on Microsoft’s web page SecurityProtocolType Enum. (Interestingly this is one of the pages that will not return a result unless you set the SecurityProtocol property.)

The example’s Form1_Load event handler uses the following statement to set the SecurityProtocol.

ServicePointManager.SecurityProtocol =
    Protocols.protocol_Tls11 | Protocols.protocol_Tls12;

The SecurityProtocol is a mask, so you can use the | operator to allow multiple protocols. This code allows TLS 1.2 and TLS 1.3.

Conclusion

So that’s the technique. Set ServicePointManager.SecurityProtocol to a combination of the protocols that you want to allow when downloading a web result. The Protocols class defines values that you can use almost as if Protocols were an enumeration.

Download the example to see additional details and to experiment on your own.


Download Example   Follow me on Twitter   RSS feed   Donate




About RodStephens

Rod Stephens is a software consultant and author who has written more than 30 books and 250 magazine articles covering C#, Visual Basic, Visual Basic for Applications, Delphi, and Java.
This entry was posted in internet, web and tagged , , , , , , , , , , , , . Bookmark the permalink.

3 Responses to Prevent “underlying connection was closed” errors in C#

  1. John Doe says:

    Hello Rod

    I’m having problems with long-running POST requests (30m-3h long) not returning a response.
    Example:
    HttpWebRequest request = (HttpWebRequest)WebRequest.Create(“https://url.com/?act=scan”); //Create request to URL.

    All headers/config are correctly set including these:

    request.KeepAlive = true;
    request.ServicePoint.MaxIdleTime = System.Threading.Timeout.Infinite;
    request.Timeout = System.Threading.Timeout.Infinite;

    The reason MaxIdleTime is set is because the request would timeout by default. The problem is that it will never return a response nor any exceptions. Just *nothing*. I’m using a backgroundworker to make the request and waiting for the response.

    Have you ever encountered this problem or have any suggestions for resolving this? I’ve done some research but I’m out of ideas.

    Also thank you for this great blog I’ve learned a lot over the years from you. I use your ListViewExtensions.cs nearly everyday still:-)

    • RodStephens says:

      Sorry but I haven’t seen that. It’s not something I do very often.

      Are you uploading something big? If so, this article may help:

      Tackling timeout issues when uploading large files with HttpWebRequest

      If you’re not working with large amounts of data, I would suspect something wrong with the connection, but I don’t know what it might be. Sorry.

      • John Doe says:

        I’m not uploading anything but rather waiting for a response. It’s the website which is doing the heavy lifting here. When I do it in a browser the website tab keeps loading until the scan is finished.

        But your link might still be relevant so I’m going to check it out.

        Thanks for responding!

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.