I have recently started a new job where WCF services are being used. I have used them in the past and am comfortable with them but from what I can recall if the client does not close the connection it has the ability to bring your service down entirely. I am aware of the proper procedure for closing the connections but if the responsibility is on the client, they may not follow the same practices and potentially have the ability to bring the service down. Is there any other way of handling the closing of the connections so that it is not reliant on the client doing the right thing? It seems odd that anyone who has access to your service has the ability to bring it down with such ease...
Thank you very much for any insights!
One option is to use session time out in the server. This actually faults the client channel.
There are only really three ways in which a session can terminated:
1) The client closes the proxy
2) The service's receiveTimeout is exceeded before the client sends another request
3) The service throws a non-fault exception which will fault the channel and so terminate the session
If you don't want the client involved then you only have 2 and 3 neither of which end well for the client - they will get an exception in both situation on the next attempt to talk to the service.
You could use Duplex messaging and get the service to notify the client that its requires session termination - the client then gets an opportunity to close down the proxy gracefully but this is a cooperative strategy
Or you need to use duplex (but still the client will have to call the service).
Here is some important points of the service implementation:
a: Use a static dictionary to keep the Client’s IP and callback channel. Before writing on the share object, lock the object.
b: Gets the IP address of the client using the GetAddressAsString method. You can get the IP of the client from the incoming message. The following statement shows how can we get the IP adddress of the Client in WCF:
RemoteEndpointMessageProperty clientEndpoint = OperationContext.Current.IncomingMessageProperties[RemoteEndpointMessageProperty.Name] as RemoteEndpointMessageProperty;
String ipAddress = clientEndpoint.Address;
If you are using the namepipe binding, you will not get the RemoteEndpointMessageProperty.
c: When the client creates the proxy of the service, it will call StartingService method immediately. Inside the StartingService method, I am keeping the callback channel of the client and current instance into the dictionary.
d: When the user of WCF service wants to disconnect a client, he/she will call the Disconnect method with the IP Address of the client.
e: The Disconnect method uses the IP Address to get the callback channel of the client and associate service instance of the client from the dictionary. Eventually, it notifies the client by using callback channel and close the incoming channel.
Here is the implementation through code:
[ServiceContract(CallbackContract=typeof(INotifyClientCallback),SessionMode=SessionMode.Required)]
public interface IService1
{
[OperationContract]
bool StartingService();
}
public interface INotifyClientCallback
{
[OperationContract(IsOneWay = true)]
void Disconnecting();
}
INotifyClientCallback interface for Callback.
Step 2: Implementation of the Contact:
[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerSession)]
public class Service1 : IService1
{
private static readonly Dictionary subscribers = new Dictionary();
public static event EventHandler onClientAdded;
///
/// Returns the IP Address of the Client
///
///
public string GetAddressAsString()
{
if (!OperationContext.Current.IncomingMessageProperties.ContainsKey(RemoteEndpointMessageProperty.Name))
{
return "127.0.0.1";
}
RemoteEndpointMessageProperty clientEndpoint =
OperationContext.Current.IncomingMessageProperties[RemoteEndpointMessageProperty.Name] as RemoteEndpointMessageProperty;
return clientEndpoint.Address;
}
public bool StartingService()
{
//Get the callback reference
INotifyClientCallback callback = OperationContext.Current.GetCallbackChannel();
string IPAddress = GetAddressAsString();
lock (subscribers)
{
if (!subscribers.ContainsKey(IPAddress))
{
subscribers[IPAddress] = new CommunicationStore()
{ NotifyCallback = callback,
IService = OperationContext.Current.InstanceContext
};
if (onClientAdded != null)
{
onClientAdded(IPAddress, null);
}
}
}
return true;
}
public static void Disconnect(string ipAddress)
{
if (subscribers.ContainsKey(ipAddress))
{
CommunicationStore com = subscribers[ipAddress];
if (((ICommunicationObject)com.NotifyCallback).State == CommunicationState.Opened)
{
try
{
//fires the callback method
com.NotifyCallback.Disconnecting();
com.IService.IncomingChannels.FirstOrDefault().Close();
}
catch (Exception)
{
throw;
}
}
}
}
}
public class CommunicationStore
{
public InstanceContext IService { get; set; }
public INotifyClientCallback NotifyCallback { get; set; }
}
Related
I haven't found a clear answer on this. so if there is already a question about this, my bad.
I have a WCF service that pushes data via a callback method to connected clients. this callback method is oneway. so everytime there is new data I loop over the connected users and push the data.
The problem I have right now is when a client disconnects it throws an error and the channel becomes faulted.
I always thought that oneway didn't care if the message arrives at the destination. So if there's no client, then bad luck. but no exception.
but there is an exception and that exception faults the channel.
Now I've read somewhere that if you enable reliable sessions, that the exception won't fault the channel. Is this true?
How can I prevent that the channel goes into faulted state when an exception happens on a oneway call?
The list of registered and avaiable clients you can store in some resource such as List. Create another interface which exposes Connect/Disconnect methods. Connect is invoked when application starts off and within method client is added to the list. Disconnect in turn is invoked when application shuts down in order to get rid client of list. OnStartup/OnClosing events or their equivalents, depending on what kind of application client is, refer to moment when application is launched and closed. Such a solution ensures that resource stores only users avaiable to be reached.
[ServiceContract]
interface IConnection
{
[OperationContract(IsOneWay = true)]
void Connect();
[OperationContract(IsOneWay = true)]
void Disconnect();
}
[ServiceContract]
interface IServiceCallback
{
[OperationContract(IsOneWay = true)]
void CallbackMethod();
}
[ServiceContract(CallbackContract = typeof(IServiceCallback))]
interface IService
{
[OperationContract]
void DoSth();
}
class YourService : IConnection, IService
{
private static readonly List<IServiceCallback> Clients = new List<IServiceCallback>();
public void Connect()
{
var newClient = OperationContext.Current.GetCallbackChannel<IServiceCallback>();
if (Clients.All(client => client != newClient))
Clients.Add(newClient);
}
public void Disconnect()
{
var client = OperationContext.Current.GetCallbackChannel<IServiceCallback>();
if (Clients.Any(cl => cl == client))
Clients.Remove(client);
}
public void DoSth()
{
foreach(var client in Clients)
client.CallbackMethod();
}
}
At the end expose another endpoint with IConnection so that client can create proxy meant to be used only for connection/disconnection.
EDIT:
I know it has been a while since I posted an answear but I did not find in order to prepare an example. The workaround is to let service's interface derive IConnection and then expose only service as an endpoint. I attach simple example of WCF and WPF app as client. Client's application violates MVVM pattern but in this case it is irrelevant. Download it here.
To add on what Maximus said.
I've implemented this pattern in a class where clients can subscribe to get updates of internal states of a system, so a monitoring client can show graphs and other clients do other stuff like enabling/disabling buttons if some state is active.
It removes faulted channels from the list when they fail. Also all current states are sent when a client connects.
here's the code, hope it helps!
[ServiceBehavior(ConcurrencyMode = ConcurrencyMode.Multiple)]
public class Publish : IPublish
{
private struct SystemState
{
public string State;
public string ExtraInfo;
}
private static Dictionary<Key<string>, IPublishCallback> mCallbacks = new Dictionary<Key<string>, IPublishCallback>();
private static Dictionary<string, SystemState> mStates = new Dictionary<string, SystemState>();
public void RegisterClient(string name, string system)
{
lock (mCallbacks)
{
IPublishCallback callback = OperationContext.Current.GetCallbackChannel<IPublishCallback>();
Key<string> key = new Key<string>(name, system);
if (!mCallbacks.ContainsKey(key))
{
mCallbacks.Add(key, callback);
}
else
{
mCallbacks[key] = callback;
}
foreach (KeyValuePair<string, SystemState> s in mStates)
{
mCallbacks[key].ServiceCallback(s.Key, s.Value.State, s.Value.ExtraInfo);
}
}
}
public void UnregisterClient(string name)
{
lock (mCallbacks)
{
outer: foreach (var key in mCallbacks.Keys)
{
if (key.Key1 == name)
{
mCallbacks.Remove(key);
goto outer;
}
}
}
}
public void SetState(string system, string state, string extraInfo)
{
lock (mCallbacks)
{
List<Key<string>> toRemove = new List<Key<string>>();
SystemState s = new SystemState() { State = state, ExtraInfo = extraInfo };
SystemState systemState;
if (!mStates.TryGetValue(system, out systemState))
mStates.Add(system, s);
else
mStates[system] = s;
foreach (KeyValuePair<Key<string>, IPublishCallback> callback in mCallbacks)
{
try
{
callback.Value.ServiceCallback(system, state, extraInfo);
}
catch (CommunicationException ex)
{
toRemove.Add(new Key<string>(callback.Key.Key1, callback.Key.Key2));
}
catch
{
toRemove.Add(new Key<string>(callback.Key.Key1, callback.Key.Key2));
}
}
foreach (Key<string> key in toRemove)
mCallbacks.Remove(key);
}
}
}
Problem:
getting a deadlock exception message in the WCF client.
Scenario:
Service calls a client callback (this call is completely independent and is initiated by some condition on the server).
While inside the client callback function, the client calls a function in the service and that's when the deadlock exception is thrown:
This operation would deadlock because the reply cannot be received until the current Message completes processing. If you want to allow out-of-order message processing, specify ConcurrencyMode of Reentrant or Multiple on CallbackBehaviorAttribute.
I tried to simplify the code as much as I can. I did read this article but still I cant find where the problem is: http://msdn.microsoft.com/en-us/library/cc294424.aspx
I'd appreciate any suggestions....
SERVICE:
[ServiceContract(Namespace = "http://abc.com/Core", SessionMode = SessionMode.Required, CallbackContract = typeof(ISvcCallback))]
public interface ISvc
{
// One way only - does not wait until operation completes and returns
// Can initiate session
[OperationContract(IsOneWay = true)]
void Initialize(string appId);
[OperationContract(IsInitiating = false)]
Account GetCurrentAccount();
}
public interface ISvcCallback
{
/// <summary>
/// Report status of the account
/// </summary>
/// <param name="acct"></param>
[OperationContract(IsOneWay=true)]
void AccountStatus(Account acct);
}
Service Implementation
[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single, ConcurrencyMode = ConcurrencyMode.Reentrant, UseSynchronizationContext = false)]
public class Svc : ISvc
{
public Account GetCurrentAccount()
{
SipAccount sipAcct = null;
try
{
Account acct = m_MyBusinessObject.GetCurrentAccount();
}
catch (Exception ex)
{
}
return Acct;
}
}
}
CLIENT:
public class CallbackHandler : WcfSipItfService.IWinSipItfCallback
{
public void AccountStatus(Account Acct)
{
try
{
// display accout status in UI by delegate-wrapped event
// delegate and event declarations are somewhere else
// and work fine...
if (DisplayAccountStatusEvent != null)
DisplayAccountStatusEvent(Acct);
}
catch (Exception ex)
{
....
}
}
private void OnDisplayAccountStatusEvent(Account acct)
{
// call service function results in deadlock
Account acct = GetCurrentAccount();
}
}
The service is Duplex - uses WSDualHttpBinding.
The deadlock appears to be due to you making a new out bound call while processing a callback from the previous call.
The error message states that you may be able to solve it by "specify ConcurrencyMode of Reentrant or Multiple on CallbackBehaviorAttribute".
Edit
I missed the code that was off the screen. Couple of things to check:
Why do you need instance mode single?
Did you updated the service references after changing the concurrency mode?
What's your client? Is it a UI client? if so, you need to add callbackBehavior UseSynchronizationContext=false to client that implements callback contract.
I am currently developing a C# Windows Form Application that I intend to let it interact with a server. The server will receive posting from a mobile application that I have developed and whenever a posting is received, my Windows Form Application should be notified and give me a notification.
E.g. My mobile application sends an message over to my server. Once my server receives the message, my windows form application should display a new notification showing the content of the message received.
I am now starting to develop my WCF Service and this is what I've done so far
[ServiceContract(Namespace = "Posting")]
public interface IPostingService
{
[OperationContract]
void NotifyAboutPosting(Posting post);
}
[DataContract]
public class Posting
{
[DataMember]
public int ID { get; set; }
[DataMember]
public string Description { get; set; }
[DataMember]
public DateTime PostingTimestamp { get; set; }
}
class Program
{
static void Main(string[] args)
{
Uri baseAddress = new Uri("http://localhost/");
ServiceHost selfHost = new ServiceHost(typeof(Posting), baseAddress);
try
{
// Step 3 of the hosting procedure: Add a service endpoint.
selfHost.AddServiceEndpoint(
typeof(IPostingService),
new WSHttpBinding(),
"Posting");
// Step 4 of the hosting procedure: Enable metadata exchange.
ServiceMetadataBehavior smb = new ServiceMetadataBehavior();
smb.HttpGetEnabled = true;
selfHost.Description.Behaviors.Add(smb);
// Step 5 of the hosting procedure: Start (and then stop) the service.
selfHost.Open();
Console.WriteLine("The service is ready.");
Console.WriteLine("Press <ENTER> to terminate service.");
Console.WriteLine();
Console.ReadLine();
// Close the ServiceHostBase to shutdown the service.
selfHost.Close();
}
catch (CommunicationException ce)
{
Console.WriteLine("An exception occurred: {0}", ce.Message);
selfHost.Abort();
}
}
}
regarding the posting class, what I want to ask is that are the methods inside used to get the information from the server?
and how do I proceed on from here after the service is done. (My winform application has been finished and all thats left is to add in this logic to receive the posting whenever the mobile app sends to the server.
and there seems to be a compilation error of
The contract name '##.IPostingService' could not be found in the list of contracts implemented by the service '##.Posting'.
could anyone help me with this? thanks a million!
Where is your actual implementation? You have contract (IPostingService), data (Posting)... but where's code doing the work? You seem to lack contract implementation:
public class PostingService : IPostingService
{
public void NotifyAboutPosting(Posting post)
{
// do something with post here
}
}
And you register actual worker class (not data) when setting up your host:
ServiceHost selfHost = new ServiceHost(typeof(PostingService), baseAddress);
I would suggest checking out these likely culprits:
WCF - Contract Name could not be found in the list of contracts
Discussion on Microsoft forums
Blog post on the issue
I want to know is there way of Event Handling in WCF.
I came across Callbacks in WCF, but i want to do Event Handling in WCF.
My requirement is like i want to raise event to particular clients not to all the clients using Event Handling in WCF and i also want to maintain session.
I have seen Publisher/Subscriber model in WCF which deals with Callback , but this model publish to all the clients who have subscribed but i want to publish only to selected clients.
I think that can be done using Events in WCF.
Client side :
public class Callbacks : IServiceCallback
{
public void CallToMyClient(string name)
{
this.CallToMyClient(name);
}
}
protected void Page_Load(object sender, EventArgs e)
{
Callbacks callback = new Callbacks();
ServiceClient client = new ServiceClient(new InstanceContext(callback));
client.SubscribeClient();
client.DoSomeWork();
}
There is no Event in WCF to notify it's client but there is a callback channel, the purpose of the callback channel is same as event though the working principle is totally different in both cases.
To notify a particular client what you could do is store callback channel of that client while subscribing to somewhere, (I prefer Dictionary in this case). Later you can pick the instance and invoke your callback method over that channel, doing so only one client will get notified.
UPDATE
If you are interested here is the code:
public interface IClientCallback
{
//Your callback method
[OperationContract(IsOneWay = true)]
void CallToMyClient(string name);
}
[ServiceContract(CallbackContract = typeof(IClientCallback))]
public interface ITestService
{
[OperationContract(IsOneWay = true)]
void SubscribeClient();
[OperationContract(IsOneWay = true)]
void DoSomeWork();
}
[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerCall, ConcurrencyMode = ConcurrencyMode.Reentrant)]
public class ServiceImplementation : ITestService
{
private static readonly List<IClientCallback> CallbackChannels = new List<IClientCallback>();
/// <summary>
/// client should call this method before being notified to some event
/// </summary>
public void SubscribeClient()
{
var channel = OperationContext.Current.GetCallbackChannel<IClientCallback>();
if (!CallbackChannels.Contains(channel)) //if CallbackChannels not contain current one.
{
CallbackChannels.Add(channel);
}
}
public void DoSomeWork()
{
//Here write your code to do some actual work
//After you done with your work notify client
//here you are calling only the first client that is registered
IClientCallback callbackChannel = CallbackChannels[0];
callbackChannel.CallToMyClient("You are the only one receving this message");
}
}
WCF Duple Operation and UI Threads By jeff.barnes
Perhaps this can help you.
The WCF doesn't support event handler. Callback channel is the way for it
If you are using WCF for RPC(as apposed to web service or rest) you can use .Net Remoting to perfrom event invocation cross process.
You cannot use events. You can use callbacks to simulate events.
So I'm hosting WCF service in a WinForms application. I have the following
[ServiceBehavior(ConcurrencyMode = ConcurrencyMode.Multiple,
InstanceContextMode = InstanceContextMode.PerCall)]
public class Test : ITest
{
public string TestIt(string input)
{
Thread.Sleep(5000);
return "test";
}
}
I'm using Named Pipes and have two instances of another application that act as clients to the above WCF service (running in a WinForms application). I thought based on the ConcurrencyMode setting of Multiple that when Client1 calls the Test Service, Client2 doesn't have to wait till the first call is complete. However, when Client1 calls TestIt, Client2 blocks until the call from Client1 is complete!?!?! Shouldn't it make a new instance each time based on the above settings?
Also, is the best way to keep a WinForms application that is hosting a WCF service responsive is by running the WCF service on a separate thread?
NOTE: Setting [CallbackBehavior(UseSynchronizationContext = false)] on the Test class does not alleviate the problem. The service still only responds to one request at a time.
Sounds like you want to set this
http://msdn.microsoft.com/en-us/library/system.servicemodel.servicebehaviorattribute.usesynchronizationcontext.aspx
to false. By default, if there is a synchronization context when service.Open() happens, WCF will pick it up and use it. But if you don't want that feature, this flag is how to turn it off.
After digging into this a bit more the only way I was able to get this to work properly was to start the ServiceHost on a separate thread in the WinForms application. If you don't do that setting the ConcurrencyMode and InstanceContextMode attributes does nothing.
I had the same problem.
My class that implemented Callback also contained methods for wcf client, so when I was calling some method from remote service and service was calling Callback method, I was creating a deadlock.
[CallbackBehavior(UseSynchronizationContext = false, ConcurrencyMode = ConcurrencyMode.Multiple)]
public class AppContext : ICustomerOrderCallback
{
//WCF Proxy client
private CustomerOrderClient _client = null;
public AppContext()
{
InstanceContext context = new InstanceContext(this);
_client = new CustomerOrderClient(context);
_client.Subscribe(); //Remote method for subscribing callback
}
public void SendMessage(string message)
{
//Calling Remote method
_client.SendMessage(message);
}
//....code
//callback method
public void OnMessageReceived(string message)
{
//.....code
}
}
So I created a separate class for callback, added attribute CallBehavior to it and everything worked OK.
public class AppContext
{
private CustomerOrderClient _client = null;
private MyCallbackClass _myCallback = null;
public AppContext()
{
_myCallback = new MyCallbackClass();
InstanceContext context = new InstanceContext(_myCallback);
_client = new CustomerOrderClient(context);
_client.Subscribe();
}
public void SendMessage(string message)
{
_client.SendMessage(message);
}
}
[CallbackBehavior(UseSynchronizationContext = false, ConcurrencyMode = ConcurrencyMode.Multiple)]
public class MyCallbackClass : ICustomerOrderCallback
{
public void OnMessageReceived(string message)
{
//.....code
}
}