Question

The underlying connection was closed: A connection that was expected to be kept alive was closed by the server.

We're getting this exception frequently when using .Net 4.0 web service client to communicate with an ONVIF network device.

Looking at the packet captures, this seems to be a device that is non compliant with the HTTP spec and closing a connection after sending the response, against the HTTP/1.1 default of keeping it alive. This results in the client (WCF) trying to reuse the connection while the server has just closed it,

Until the manufacturer can fix this, is there any way I can tell the web service/SOAP client NOT to use persistent connections?

Note that modifying the header to use Connection: Close won't help unless as it's being closed anyway, but the SOAP client is expecting it to stay open.

Was it helpful?

Solution 2

The underlying HttpWebRequests are created in the HttpChannelFactory used by the generated client classes. This is created from the HttpTransportBindingElement which exposes a KeepAliveEnabled property.

The binding element is created internally in the WSHttpBinding class, and can be changed by overriding GetTransport().

private class WSHttpBindingNoKeepAlive : WSHttpBinding {
    public WSHttpBindingNoKeepAlive(SecurityMode securityMode)
        : base(securityMode) {
    }

    protected override TransportBindingElement GetTransport() {
        TransportBindingElement transport = base.GetTransport();
        if (transport is HttpTransportBindingElement) {
            ((HttpTransportBindingElement)transport).KeepAliveEnabled = false;
        }
        return transport;
    }
}

This overriden ...Binding class can then be used like:

WSHttpBindingNoKeepAlive clientBinding = new WSHttpBindingNoKeepAlive(SecurityMode.None);
EndpointAddress address = new EndpointAddress(this.deviceUrl);
WSNameSpace.WSClient client = new WSNameSpace.WSClient(clientBinding, address);

OTHER TIPS

but is there any way I can tell the web service/SOAP client NOT to use persistent connections?

Yes, you can set InstanceContextMode to PerCall, it will create new InstanceContext object is created prior to and recycled subsequent to each call.

In other words, When we configure a WCF service as per call, new service instances are created for every method call you make via a WCF proxy client.

You can use it like :

  • setting in ServiceBehavior over Contract Interface implementation like:

    [ServiceBehavior(InstanceContextMode=InstanceContextMode.PerCall)] public class CalculatorService : ICalculator { ... }

UPDATE --

As per your comment, it seems the solution is to explicitly set the KeepAlive property to FALSE.

It can be done in multiple ways:

  1. Code
  2. IIS Settings
  3. Web.config

Code

I actually don't know how much control you have over code.But at the client end where you are consuming service, we can change this behavior like:

protected override System.Net.WebRequest GetWebRequest(Uri uri)
{
    System.Net.HttpWebRequest webRequest = (System.Net.HttpWebRequest)base.GetWebRequest(uri);
    webRequest.KeepAlive = false;
    return webRequest;
}

Or

namespace YourNamespace
{
    using System.Diagnostics;
    using System.Web.Services;
    using System.ComponentModel;
    using System.Web.Services.Protocols;
    using System;
    using System.Xml.Serialization;
    /// <summary>
    /// This partial class makes it so all requests specify
    /// "Connection: Close" instead of "Connection: KeepAlive" in the HTTP headers.
    /// </summary>
    public partial class YourServiceNameWse : Microsoft.Web.Services3.WebServicesClientProtocol
    {
        protected override System.Net.WebRequest GetWebRequest(Uri uri)
        {
            System.Net.HttpWebRequest webRequest = (System.Net.HttpWebRequest)base.GetWebRequest(uri);
            webRequest.KeepAlive = false;
            return webRequest;
        }
    }
}

IIS Settings

  1. Open Internet Information Services (IIS) Manager:
  2. If you are using Windows Server 2008 or Windows Server 2008 R2:
    • On the taskbar, click Start, point to Administrative Tools, and then click Internet Information Services (IIS) Manager.
    • If you are using Windows Vista or Windows 7: On the taskbar, click Start, and then click Control Panel.
    • Double-click Administrative Tools, and then double-click Internet Information Services (IIS) Manager.
    • In the Connections pane, go to the site, application, or directory for which you want to enable HTTP keep-alives.
    • In the Home pane, double-click HTTP Response Headers.
    • In the HTTP Response Headers pane, click Set Common Headers... in the Actions pane.
    • In the Set Common HTTP Response Headers dialog box, uncheck the box to disable HTTP keep-alives, and then click OK.

Additionally you can set for particular website on IIS using CommandLine like:

appcmd.exe set config "<Your Web Site Here>" -section:system.webServer/httpProtocol /allowKeepAlive:"False"

Web.Config

<configuration>
   <system.webServer>
      <httpProtocol allowKeepAlive="false" />
   </system.webServer>
</configuration>

I hope it can help you in some way.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top