WCF includes support for establishing a security session through a simple configuration attribute. The primary reason of a security session is a shared security context which enables clients and services to use a faster, symmetric cryptographic exchange.
WCF sessions should not be thought of in terms of HTTP based sessions, since the former are initiated by clients and the latter by the servers. In other terms, WCF sessions are there to support some kind of shared context between a particular client and a service. This context can be anything, and is not limited to security contexts.
The attribute that establishes a security session and shared context is called, well, establishSecurityContext and is present in binding configuration. An example of such a binding would be:
<security mode ="Message">
<message clientCredentialType="Certificate" establishSecurityContext="true"/>
This binding allows HTTP based communication, demands message based security (think WS-Security) and uses certificates to sign/encrypt the message content. The attribute establishSecurityContext is set to true, which actually enforces a WS-SecureConversation session between the client and the service.
The following is a simplified version of what is going on under the covers:
- Client instantiates the service proxy
No message exchange is taking place yet.
- Client requests a SCT (Secure Context Token)
This is done by the infrastructure, when the first service method is called. SCT (again simplified) represents a secure context, which includes the symmetric key. The message which demands it is (per WS-SecureConversation spec) called RST (Request Secure Token).
- Service responds with RSTR (Request Secure Token Response) message
Session bootstraps and is ready to piggyback all further message exchanges.
What is not well known is that there is a very low limit on the number of sessions a service is willing to accept. The default is set to 10 sessions and this was changed (from 64) late in the WCF development cycle (summer 2006). So RTM ships with this default.
Service session count is greatly influenced by the instancing scheme the service is using. Since instancing is a completely different beast, let's leave this for another post (uhm, I already wrote something here). Let's just say that hitting the session problem is a non-issue when using singleton instancing (InstanceContextMode = InstanceContextMode.Single).
The main issue is, that most developers think of
Indigo WCF services in terms of simple request-response semantics and forget that such sessions get queued up on the service side if you do not terminate them appropriately.
This is the default service throttling behavior in the shipping version of WCF:
Every service is throttled, even if you don't specify it. You can, of course, override all three throttling parameters.
Sessions can only be initiated by the client. They can be explicitly terminated only by the client. There are three ways a session can get terminated:
- Explicitly by the client
- Implicitly by a timeout
- Implicitly by errors
Timeout can pass on a client or a service and, if the timeout happens, the transport channel that ensured communication gets faulted (Channel.State = CommunicationState.Faulted). Session is also terminated if an error is detected, thus invalidating the possibility to continue.
Remember that every service proxy you use will demand a new session on the service side (when using
sessionful services). If you spawn 11 threads and use a non-singleton proxy you will cause 10 sessions on the service side. The 11th will not get setup and will block the client thread until the timeout expires. In this case, the WCF infrastructure on the service side will issue a warning in the trace log, but nothing will be returned to the client. It seems as if the service would stop working. Nada.
There is a method on every service proxy and it's there for a reason. The method is called Close(). It closes the transport channel graciously and terminates the security session, thus freeing up service resources. The same happens for reliable messaging session or a combination of both.
Note: Another message (pair) is exchanged on Close(). This message is saying "We are done." to the service. Thus, one should be cautious when calling Close() for any exceptions, like CommunicationObjectFaultedException.
The best practice is to catch any CommunicationException exceptions when closing the channel.
To illustrate this, consider the following. You call a single method on a sessionful service. Then hang on to the service proxy instance for an hour. The inactivity timeout (attribute inactivityTimeout on a RM binding) is set to 10 minutes. So, ten minutes after the first call the channel gets faulted. Then you call Close(). This call will fail and throw an exception.
The following is the expected way of communication with sessionful services:
ServiceClient proxy = new ServiceClient("<Configuration>");
// call service methods
int intSum = proxy.Add(1, 2);
// ... call all other methods ...
// call service methods
int intSum = proxy.Subtract(1, 2);
// close session
catch (TimeoutException ex)
// handle timeout exceptions
catch (FaultException<T1> ex)
// handle typed exception T1
catch (FaultException<Tn> ex)
// handle typed exception Tn
catch (FaultException ex)
// handle all other typed exceptions
catch (CommunicationException ex)
// handle communication exceptions
Note that using the using statement is not recommended. The problem of using statement in this case is that CLR automatically calls Dispose() method on the using variable when exiting the statement. Since Close() can fail (remember, another message is exchanged), you can miss this important exception.
Everything in this post is true for all kinds of WCF sessions. It is not limited to security or RM sessions only.