Reputation:
I have a simple WCF duplex TCP service that I am trying to stop programmatically. If I don't have any connected users, ServiceHost.Close() is very quick but if I even have one connected user, I find the Close() function to take quite a bit of time, sometimes >30seconds. Is this usual behavior?
On the other hand, Abort() is almost instantaneous and I am tempted to use that instead.
Upvotes: 6
Views: 10595
Reputation: 2613
I noticed this problem as well. My code looks originally looked like this :
[TestMethod]
[Timeout(2000)]
public void ApiClientTest()
{
bool ApiSuccessSet, ClientSuccessSet = ApiSuccessSet = false;
Api apiService = new ApiTestService();
var clientService = new ClientTestService();
ServiceHost clientHost = new ServiceHost(clientService, new Uri(PipeService));
ServiceHost apiHost = new ServiceHost(apiService, new Uri(PipeService));
//To let us know the services were successfully opened
clientHost.Opened += (s, e) => ClientSuccessSet = true;
apiHost.Opened += (s, e) => ApiSuccessSet = true;
clientHost.AddServiceEndpoint(typeof(IClientService), new NetNamedPipeBinding(), ClientPipeServiceName);
apiHost.AddServiceEndpoint(typeof(IApiService), new NetNamedPipeBinding(), ApiPipeServiceName);
clientHost.BeginOpen(OnOpen, clientHost);
apiHost.BeginOpen(OnOpen, apiHost);
//This allows both services to be open for communication.
while (!ApiSuccessSet || !ClientSuccessSet)
Thread.Yield();
EndpointAddress ApiEndpoint = new EndpointAddress(PipeService + @"/" + ApiPipeServiceName);
EndpointAddress clientEndpoint = new EndpointAddress(PipeService + @"/" + ClientPipeServiceName);
InstanceContext context = new InstanceContext((IClientCallback)new TestClientCallback());
var ClientChannelFactory = new DuplexChannelFactory<IClientService>(context, new NetNamedPipeBinding(), clientEndpoint);
var ApiChannelFactory = new ChannelFactory<IApiService>(new NetNamedPipeBinding(), ApiEndpoint);
var ClientChannel = ClientChannelFactory.CreateChannel();
var ApiChannel = ApiChannelFactory.CreateChannel();
clientHost.Close();
apiHost.Close();
}
void OnOpen(IAsyncResult ar)
{
ServiceHost service = (ServiceHost)ar.AsyncState;
service.EndOpen(ar);
}
I noticed that the this code took 20 secondsto run. I then decided to close the channel factories like this :
[TestMethod]
[Timeout(2000)]
public void ApiClientTest()
{
bool ApiSuccessSet, ClientSuccessSet = ApiSuccessSet = false;
Api apiService = new ApiTestService();
var clientService = new ClientTestService();
ServiceHost clientHost = new ServiceHost(clientService, new Uri(PipeService));
ServiceHost apiHost = new ServiceHost(apiService, new Uri(PipeService));
//To let us know the services were successfully opened
clientHost.Opened += (s, e) => ClientSuccessSet = true;
apiHost.Opened += (s, e) => ApiSuccessSet = true;
clientHost.AddServiceEndpoint(typeof(IClientService), new NetNamedPipeBinding(), ClientPipeServiceName);
apiHost.AddServiceEndpoint(typeof(IApiService), new NetNamedPipeBinding(), ApiPipeServiceName);
clientHost.BeginOpen(OnOpen, clientHost);
apiHost.BeginOpen(OnOpen, apiHost);
//This allows both services to be open for communication.
while (!ApiSuccessSet || !ClientSuccessSet)
Thread.Yield();
EndpointAddress ApiEndpoint = new EndpointAddress(PipeService + @"/" + ApiPipeServiceName);
EndpointAddress clientEndpoint = new EndpointAddress(PipeService + @"/" + ClientPipeServiceName);
InstanceContext context = new InstanceContext((IClientCallback)new TestClientCallback());
var ClientChannelFactory = new DuplexChannelFactory<IClientService>(context, new NetNamedPipeBinding(), clientEndpoint);
var ApiChannelFactory = new ChannelFactory<IApiService>(new NetNamedPipeBinding(), ApiEndpoint);
var ClientChannel = ClientChannelFactory.CreateChannel();
var ApiChannel = ApiChannelFactory.CreateChannel();
ClientChannelFactory.Close();
ApiChannelFactory.Close();
clientHost.Close();
apiHost.Close();
}
This leads me to believe that the long time it takes is disposing the client's instance context.
I suspect that there are 3 ways to handle this solution better.
The first is to create a function on the client that manages ending the session. This way you can call that method before the service plans to shut down and it will speed up the shut down time.
the second is to close asynchronously and do other processing while the connection is closing.
The third is to program into the client when to close the connection (especially if you control both the client and the service) so that the client can terminate the session itself and the service can shut down gracefully and quickly.
Upvotes: 0
Reputation: 19117
It may be. The docs state that
The Close method allows any unfinished work to be completed before returning. For example, finish sending any buffered messages.
There is an overload to Close()
which takes a TimeSpan
(and throws
if the timespan is exceeded)
Abort()
looks like the best way to stop a WCF host without delay.
Upvotes: 10
Reputation: 1784
Make sure you're closing the client-side connection, like this:
var channel = factory.CreateChannel();
var channel.DoSomethingForMe();
(channel as ICommunicationObject).Close();
If you don't do this Close() on the channel, Close() on the server waits a very, very long time, even if you specify a short timeout.
Upvotes: 8
Reputation: 934
I could see the benefit of Abort() over Close() for expediency, but I would imagine something bad may happen. In my case, I want to wait for Close() so I can reuse the port(s). This code will wait for the services to be actually closed before resuming.
Semaphore cont=new Semaphore(0, 1);
foreach (ServiceHost s in WCFServices) {
try {
s.Closed += delegate(Object o, System.EventArgs n) {
cont.Release();
};
s.Close();
cont.WaitOne();
} catch (Exception) {
}//try
}//for
Upvotes: 0
Reputation: 53606
If you are ok with killing any in-progress service calls then Abort() is the way to go. Close() is the polite way of closing down a service.
Upvotes: 1