Do i need to open each client message in new thread? - c#

I have application that host WCF service:
namespace ServiceLibrary
{
public delegate void StatusEventHandler(Capture capture);
// You have created a class library to define and implement your WCF service.
// You will need to add a reference to this library from another project and add
// the code to that project to host the service as described below. Another way
// to create and host a WCF service is by using the Add New Item, WCF Service
// template within an existing project such as a Console Application or a Windows
// Application.
[ServiceContract()]
public interface IService1
{
[OperationContract]
string ClientMsg(string str);
}
[ServiceBehavior(
ConcurrencyMode = ConcurrencyMode.Multiple,
InstanceContextMode = InstanceContextMode.PerSession)]
public class service1 : IService1
{
public event StatusEventHandler CapturingEvent;
public event StatusEventHandler OnProcessExitedEvent;
public event StatusEventHandler OnFinishEvent;
public string ClientMsg(string str)
{
return DoWork(str);
}
private DoWork(string str)
}
MyClass obj = New MyClass();
obj.Start(str); /// Do my job
}
}
}
The client send string to my server and i am opening instance of my class and this class open process, do my job and return to the client the process id number.
this server received messages from multiple clients so i wonder if i need to open new thread each time i received client message to avoid situation that several clients send message to the server in the same time.
This is how i am open the server connection in main form:
private void connect()
{
try
{
if (!isConnected)
{
// Returns a list of ipaddress configuration
IPHostEntry ips = Dns.GetHostEntry(Dns.GetHostName());
// Get machine ipaddress
IPAddress _ipAddress = IPAddress.Parse(tbServerIp.Text);
// Create the url that is needed to specify where the service should be started
urlService = "net.tcp://" + _ipAddress.ToString() + ":8000/CapturesService";
// Instruct the ServiceHost that the type that is used is a ServiceLibrary.service1
host = new ServiceHost(typeof(ServiceLibrary.service1));
//ServiceLibrary.service1 serviceInstance = new ServiceLibrary.service1();
//serviceInstance.CapturingEvent += serviceInstance_StartCapturingEvent;
//serviceInstance.OnProcessExitedEvent += serviceInstance_OnProcessExitedEvent;
//host = new ServiceHost(serviceInstance);
host.Opening += new EventHandler(host_Opening);
host.Opened += new EventHandler(host_Opened);
host.Closing += new EventHandler(host_Closing);
host.Closed += new EventHandler(host_Closed);
// The binding is where we can choose what transport layer we want to use. HTTP, TCP ect.
NetTcpBinding tcpBinding = new NetTcpBinding();
tcpBinding.TransactionFlow = false;
tcpBinding.Security.Transport.ProtectionLevel = System.Net.Security.ProtectionLevel.EncryptAndSign;
tcpBinding.Security.Transport.ClientCredentialType = TcpClientCredentialType.Windows;
tcpBinding.Security.Mode = SecurityMode.None; // <- Very crucial
// Add a endpoint
host.AddServiceEndpoint(typeof(ServiceLibrary.IService1), tcpBinding, urlService);
// A channel to describe the service. Used with the proxy scvutil.exe tool
ServiceMetadataBehavior metadataBehavior;
metadataBehavior = host.Description.Behaviors.Find<ServiceMetadataBehavior>();
if (metadataBehavior == null)
{
// Create the proxy object that is generated via the svcutil.exe tool
metadataBehavior = new ServiceMetadataBehavior();
metadataBehavior.HttpGetUrl = new Uri("http://" + _ipAddress.ToString() + ":8001/CapturesService");
metadataBehavior.HttpGetEnabled = true;
metadataBehavior.ToString();
host.Description.Behaviors.Add(metadataBehavior);
urlMeta = metadataBehavior.HttpGetUrl.ToString();
}
host.Open();
isConnected = true;
}
else
{
if (asyncWorker.IsBusy)
{
// Notify the worker thread that a cancel has been requested.
// The cancel will not actually happen until the thread in the
// DoWork checks the bwAsync.CancellationPending flag, for this
// reason we set the label to "Cancelling...", because we haven't
// actually cancelled yet.
asyncWorker.CancelAsync();
}
host.Close();
isConnected = false;
}
}
catch (Exception ex)
{
isConnected = false;
MessageBox.Show(ex.Message);
return;
}
}
private int StartTsharkProcess(Capture capture)
{
ProcessExitedEvent += Capture_ProcessExitedEvent;
string args = string.Format("-i {0} host {1} {2} -a duration:300 -w {3}",
Interface.interfaceNumber,
capture.machineIpAddress,
getTsharkFilter(),
Path.Combine(LocalPath.localPath, capture.fileName));
int processId = InvokeProcess(WiresharkProcesses.Tshark, args);
return processId;
}

this server received messages from multiple clients so i wonder if i need to open new thread each time i received client message to avoid situation that several clients send message to the server in the same time.
ServiceBehavior attribute has ConcurrencyMode Property.
This property indicates whether an instance of a service can handle one thread or multiple threads that execute concurrently, and if single-threaded, whether reentrancy is supported.
The default service behavior has ConcurrencyMode with ConcurrencyMode.Single value. So, if it is necessary to allow multiple calls at once, please use ConcurrencyMode.Multiple with notice:
No synchronization guarantees are made. Because other threads can change your service object at any time, you must handle synchronization and state consistency at all times.
Note: if service methods perform long-running tasks, clients might have a timeout.

Related

How can I stop a WCF Service from being recreated for each request?

I have a service that I need to run that manages long running background tasks. I am hosting this service through the WCF, but I'm running into a problem where the service keeps getting created and destroyed with every subsequent web request.
Here is the service:
[ServiceContract]
public interface IFileProcessService
{
[OperationContract]
[WebInvoke(UriTemplate = "ProcessFile?s={session}&file={fileName}", BodyStyle = WebMessageBodyStyle.Bare)]
int ProcessFile(int session, string fileName);
}
public class FileProcessService : IFileProcessService
{
private FileProcessTaskScheduler mTaskScheduler;
private TaskFactory mTaskFactory;
private FileProcessService()
{
mTaskScheduler = new FileProcessTaskScheduler(4);
mTaskFactory = new TaskFactory(mTaskScheduler);
}
public int ProcessFile(int scriptRunId, string fileName)
{
return mTaskFactory.StartNew(() =>
{
Console.WriteLine("Processing file {0} for script run {1}", fileName, scriptRunId);
Thread.Sleep(TimeSpan.FromMinutes(1));
Console.WriteLine("Completed processing file {0} for script run {1}", fileName, scriptRunId);
}).Id;
}
}
Obviously with the TaskScheduler and TaskFactory in there it doesn't really work when it gets disposed of at the end of every request.
Over in main I host the service like so:
WebServiceHost host = new WebServiceHost(typeof(FileProcessService), new Uri("http://localhost:7343/"));
ServiceDebugBehavior sdb = host.Description.Behaviors.Find<ServiceDebugBehavior>();
sdb.HttpHelpPageEnabled = false;
ServiceEndpoint ep = host.AddServiceEndpoint(typeof(IFileProcessService), new WebHttpBinding(), "");
host.Open();
Console.WriteLine("Service is now running...");
Console.ReadKey();
host.Close();
Console.WriteLine("Service has stopped...");
I have tried making the members of the service static and then just wrapping their instantiation in the constructor with if checks, but at that point I think it would just be cleaner to write a separate singleton class to handle that stuff.
The WCF WebServiceHost seems to refer to the instance of the service that it hosts as a singleton, but it certainly isn't treating it as such. Is there some extra step I have to take to make the WebServiceHost NOT dispose of my object after every request?
Take a look at the ServiceBehaviorAttribute InstanceContextMode
You can see a working example of this type of WCF service here and this is where it's used in the code.

ServiceHost fail to Open() again with new ServiceHost instance when it failed for the first time in WCF self-host

My symptom is exactly as this post described http://social.msdn.microsoft.com/Forums/vstudio/en-US/302ca96e-a810-4958-9905-90ba1175765d/servicehost-does-not-detech-endpoints-cant-recover-from-a-faulted-state
I wonder if this is a known bug.
My code is slightly different from Dave's whereas his ServiceHost instance (named WebService) is outside of Start() method. My ServiceHost instance (named host) is declared inside. When debugging, I check in Host Description that the address of endpoints has changed to the correct IP. However, the Open() still throws an exception with the old wrong IP address.
private bool InitHost(PtiType type, string serverIp, int portNumber)
{
if (!HostDictionary.ContainsKey(type))
{
Uri addressBase = new Uri(String.Format("net.tcp://{0}:{1}/CommunicationService/{2}", serverIp, portNumber.ToString(), type.ToString()));
var service = new PtiCommunicationService(type);
service.ClientConnected += service_ClientConnected;
service.ClientBroadcasted += service_ClientBroadcasted;
service.ClientSentTo += service_ClientSentTo;
service.ClientDisconnected += service_ClientDisconnected;
var host = new ServiceHost(service, addressBase);
//For publishing metadata only
//Define Metadata endPoint, So we can publish information about the service
ServiceMetadataBehavior mBehave = new ServiceMetadataBehavior();
host.Description.Behaviors.Add(mBehave);
//Enable debug info in fault
((ServiceDebugBehavior)host.Description.Behaviors[typeof(ServiceDebugBehavior)]).IncludeExceptionDetailInFaults=true;
host.AddServiceEndpoint(typeof(IPtiCommunication), new NetTcpBinding(SecurityMode.None), "");
host.AddServiceEndpoint(typeof(IMetadataExchange),
MetadataExchangeBindings.CreateMexTcpBinding(),
"mex");
try
{
host.Open();
//Add host to dictionary to keep track
HostDictionary.Add(type, host);
LogList.Add(String.Format("{0}\tThe service {1} at {2} is ready", DateTime.Now.ToLongTimeString(), service.ServiceType.ToString(), serverIp));
string hostInfo = String.Format("{0}\tHost information:\n", DateTime.Now.ToLongTimeString());
hostInfo += "Enpoints details:\n";
foreach (var endpt in host.Description.Endpoints)
{
hostInfo += String.Format("\t Name:\t\t{0}\n", endpt.Name);
hostInfo += String.Format("\t Logical address:\t{0}\n", endpt.Address);
hostInfo += String.Format("\t Physical address:\t{0}\n", endpt.ListenUri);
hostInfo += String.Format("\t Binding:\t\t{0}\n", endpt.Binding);
hostInfo += String.Format("\t Contract:\t{0}\n\n", endpt.Contract.ContractType.Name);
}
LogList.Add(hostInfo);
}
catch (Exception e)
{
host.Abort();
host = null;
LogList.Add(String.Format("{0}\t{1}", DateTime.Now.ToLongTimeString(), e.Message));
}
return true;
}
return false;
}
Here is my ServiceBehavior for PtiCommunicationService
[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single,
ConcurrencyMode = ConcurrencyMode.Multiple, UseSynchronizationContext = false)]
and ServiceContract for the interface
[ServiceContract(CallbackContract = typeof(IPtiCommunicationCallback), SessionMode = SessionMode.Required)]
There is no other configurations in app.config. All are in the code.
Thanks
UPDATE:
I've discovered that both of us use the same overload for ServiceHost Constructor (Object, Uri[]) which make a singleton of web service.
When we create new Service Host with the same singleton, somehow changing endpoint address doesn't take into affect because the instance of the service is still there even though the host has been aborted.
Is there any solution to clean up that singleton when we create new host?
That's my suspicion so far, please correct me if I'm wrong.
Apparently WCF caches some socket information. The best workaround I have found was given by Ivan here https://stackoverflow.com/a/6839265/955400 . You check if you can open the connection before attempting to call host.Open().

WCF right solution for Publish/subscribe pattern implementation

I implemented my own code of Publish/subscribe pattern implementation with WCF with WSDualHttpBinding, but i've a little problem with timeouts that i explain later, for now let me show what i'm doing:
public interface IEventSubscriber
{
[OperationContract]
void NotifyEvent(EventNotification notification);
[OperationContract]
void NotifyServiceDisconnecting();
}
[ServiceContract(SessionMode = SessionMode.Required, CallbackContract = typeof(IEventSubscriber))]
public interface IEventPublisherService
{
[OperationContract(IsOneWay = false, IsInitiating = true)]
void Subscribe(string culture);
[OperationContract(IsOneWay = false, IsInitiating = false, IsTerminating = true)]
void Unsubscribe();
}
[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single)]
internal class EventPublisherServiceImpl : IEventPublisherService
{
ServiceHost host;
public bool StartService()
{
bool ret = false;
try
{
Uri baseAddress = new Uri(ConfigurationManager.AppSettings[GlobalConstants.CfgKeyConfigEventPublishserServiceBaseAddress].ToString());
EventHelper.AddEvent(string.Format("Event Publisher Service on: {0}", baseAddress.ToString()));
host = new ServiceHost(this, baseAddress);
// duplex session enable http binding
WSDualHttpBinding httpBinding = new WSDualHttpBinding(WSDualHttpSecurityMode.None);
httpBinding.ReceiveTimeout = TimeSpan.FromMinutes(10);
httpBinding.ReliableSession = new ReliableSession();
httpBinding.ReliableSession.Ordered = true;
httpBinding.ReliableSession.InactivityTimeout = TimeSpan.FromMinutes(10);
host.AddServiceEndpoint(typeof(IEventPublisherService), httpBinding, baseAddress);
// Enable metadata publishing.
ServiceMetadataBehavior smb = new ServiceMetadataBehavior();
smb.HttpGetEnabled = true;
smb.MetadataExporter.PolicyVersion = PolicyVersion.Policy15;
host.Description.Behaviors.Add(smb);
// Open the ServiceHost to start listening for messages.
host.Open();
ret = true;
}
catch (Exception e)
{
EventHelper.AddException(e.Message);
}
return ret;
}
...
}
now in my implementation class i have a list of subscribers that are stored in memory, when a new notification arrived the following code is performed for each subscriber:
...
/// <summary>
/// List of active subscribers
/// </summary>
private static Dictionary<IEventSubscriber, string> subscribers = new Dictionary<IEventSubscriber, string>();
...
that i use it like this:
internal void Subscribe(string culture)
{
lock (subscribers)
{
// Get callback contract as specified on the service definition
IEventSubscriber callback = OperationContext.Current.GetCallbackChannel<IEventSubscriber>();
// Add subscriber to the list of active subscribers
if (!subscribers.ContainsKey(callback))
{
subscribers.Add(callback, culture);
}
}
}
...
private void OnNotificationEvent(NormalEvent notification)
{
lock (subscribers)
{
List<IEventSubscriber> listToRemove = new List<IEventSubscriber>();
// Method signature: Parallel.ForEach(IEnumerable<TSource> source, Action<TSource> body)
Parallel.ForEach(subscribers, kvp =>
{
try
{
kvp.Key.NotifyEvent(new EventNotification(notification, kvp.Value));
}
catch (Exception ex)
{
EventHelper.AddException(string.Format("Error notifying event notification to client: {0} - removing this one", ex.Message));
listToRemove.Add(kvp.Key);
}
} //close lambda expression
); //close method invocation
Parallel.ForEach(listToRemove, subs =>
{
try
{
subs.NotifyServiceDisconnecting();
}
catch (Exception ex) {
EventHelper.AddException(string.Format("Failed to notify client that is to be removed: {0}",
ex.Message));
}
subscribers.Remove(subs);
}
);
}
}
What is the problem with this, when timeouts are achieved (note that i set 10 minutes for ReceiveTimeout and inactive timeout) the subscribers that are in the list go to fault state, and the following exception is catched in the OnNotificationEvent
*The operation 'NotifyEvent' could not be completed because the sessionful channel timed out waiting to receive a message. To increase the timeout, either set the receiveTimeout property on the binding in your configuration file, or set the ReceiveTimeout property on the Binding directly. *
Ok i can increase the timeout value, but if i do this it will happen some time, eventually.
My question are: i'm doing something wrong when trying to implement this pattern? or exists any other way of implementing this pattern a better way that avoid this problem? or exist any way of reconnecting the faulted callback channel (for what i'm reading it's not possible, but due to that i can't notify the client that is connection was lost, letting the client blind not knowing that the communication ended!? or is a way to give knowledge that he lost communication with the publisher!?)
Of course solution like ping messages are out of date :) but ok, if nothing better appears look like i've to implement something like that...
Thanks
For now the solution was to change the timeouts to have a infinite value:
// duplex session enable http binding
WSDualHttpBinding httpBinding = new WSDualHttpBinding(WSDualHttpSecurityMode.None);
httpBinding.ReceiveTimeout = TimeSpan.MaxValue;
httpBinding.ReliableSession = new ReliableSession();
httpBinding.ReliableSession.Ordered = true;
httpBinding.ReliableSession.InactivityTimeout = TimeSpan.MaxValue;
You are using Parallel.ForEach but I'm not sure that this is enough. AFAIR Parallel.ForEach does not execute each iteration in the separate thread.
I would like to suggest to start separate threads for each subscriber in the OnNotificationEvent and use lock to make sure that foreach will not be breaked by Collection modified exception:
lock (_subscribersSync)
foreach (var chatter in subscribers)
{
Logger.Log.DebugFormat("putting all users to {0}", subscribers.Key.Name);
Thread th = new Thread(PublishAllUserMessage);
th.Start(new MessageData() { Message = "", Subscriber = chatter.Key};
}
void PublishAllUserMessage(object messageData)
{
MessageData md = (MessageData)messageData;
try
{
md.Subscriber.NotifyEvent(...event parameters here ...);
}
catch (Exception ex)
{
Logger.Log.Error(string.Format("failed to publish message to '{0}'", md.Subscriber.Name), ex);
KickOff(md.Subscriber);
}
}
object _subscribersSync = new object();
void KickOff(IEventSubscriber p)
{
lock (_subscribersSync)
{
subscribers.Remove(p);
Logger.Log.WarnFormat("'{0}' kicked off", p.Name);
}
}
public class MessageData
{
public string Message;
public IEventSubscriber Subscriber;
}

How do I set permissions for a WCF Windows Service?

I have a Windows service that exposes a WCF service.
I also have an application that talks to the WCF service and sends commands and receives data back.
All this works fine when I run the application from Visual Studio.
However, when I install the application and run it, the application cannot communicate with the service.
Nor can the batch files that the application runs to Stop and Start the service.
I've tried using netsh to 'reserve' the URI but I don't really know what I'm doing :-)
Can you point me in the right direction?
Windows Server code WCF Service code (abridged):
// NOTE: You can use the "Rename" command on the "Refactor" menu to change the class name "WCF_Service" in both code and config file together.
[ServiceContract]
public class InternetGauge_IO
{
[OperationContract]
public void Pause()
{
RunningState.paused = true;
}
[OperationContract]
public void Continue()
{
RunningState.paused = false;
}
[OperationContract]
public bool GetRunningState()
{
return RunningState.paused;
}
....
Windows Server code WCF Create endpoint code:
private void InitializeConsoleComms()
{
try
{
//Prepare comms with the Console application
Type serviceType = typeof(InternetGauge_IO);
host = new ServiceHost(serviceType, new Uri[] { new Uri("http://localhost:8080/") });
// Add behavior for our MEX endpoint
ServiceMetadataBehavior behavior = new ServiceMetadataBehavior();
behavior.HttpGetEnabled = true;
host.Description.Behaviors.Add(behavior);
// Create basicHttpBinding endpoint at http://localhost:8080/RJB.InternetGauge/
host.AddServiceEndpoint(serviceType, new BasicHttpBinding(), "RJB.InternetGauge");
// Add MEX endpoint at http://localhost:8080/MEX/
host.AddServiceEndpoint(typeof(IMetadataExchange), new BasicHttpBinding(), "MEX");
host.Open();
logger.LogEvent("Console comms ready", "Internet Gauge", 4, 1);
}
catch (Exception e)
{
logger.LogEvent("Failed to open Console comms", "Internet Gauge", 1, 1);
logger.LogEvent("Exception : " + e.InnerException + " Stack Trace: " + e.StackTrace + " Message: " + e.Message, "RJB.InternetGauge.WindowsService.Main", 1, 1);
}
}
The application just uses the generated proxy and methods e.g.
private bool GetRunningState()
{
try
{
InternetGauge_IOClient client = new InternetGauge_IOClient();
isRunning = true;
return(client.GetRunningState());
}
catch (Exception)
{
trayIcon.Text = "Internet Gauge Windows Service does not appear to be running.";
trayIcon.Icon = RJB.InternetGauge.Console.Properties.Resources.ServiceStopped;
isPaused = true;
isRunning = false;
return isPaused;
}
}
The netsh command I tried
netsh http add urlacl url=http://+:8080/InternetGauge_IO user=PC-01\richard
Any ideas?
Thanks
Richard
It is because I am a twit.
Didn't copy the app.config over during the installation program.
All works fine now.

WCF ServiceHost.Close() Delay

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.
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.
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.
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.
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
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.

Categories