Introduction
I’ve used TCP Transport to make my projects work. Some of my projects are working seperately on different machines which use WCF to get communication with each other. For exemple, we need get Market Data from Bloomberg’s service (it’s legally), and then transfer real-time these datas to other apps which are installed on my colleagues’ PC. In this case, WCF Tcp transport is really a good choice. But i even use TCP transport when the datas are only needed to be transferred in the same PC. In fact, it works well as i wished. But i ignored an other problem is that anyone else can also access this PC’s WCF service. It could be a very dangerous if an hacker want our bussiness down. I revised once WCF transports and i found another method which might be help to escape from hacker’s attack. That’s WCF Named Pipe Tranport.
MSDN’s definition, when to use the Named Pipe Transport ?
- A named pipe is an object in the Windows operating system kernel, such as a section of shared memory that processes can use for communication. A named pipe has a name, and can be used for one-way or duplex communication between processes on a single machine.
- When communication is required between different WCF applications on a single computer, and you want to prevent any communication from another machine, then use the named pipes transport. An additional restriction is that processes running from Windows Remote Desktop may be restricted to the same Windows Remote Desktop session unless they have elevated privileges.
A named pipe itself is just an object in the window kernel. It provides access for different clients to a shared resource. A pipe is identified by its name and can be used for one-way or duplex communication scenarios. While there may be only one pipe server, there may be more than one client applications using a pipe. With the NetNamedPipeBinding class, WCF offers an abstraction to pipe objects, making it really easy to use them for inter-process communication. I’m trying write a sample. I encountered lot of questions. I looked up several articles and the book written by Juval Löwy, I found some good articles : Communication options with WCF – Part 2, WCF by example on netNamedPipeBinding, Inter-Process Duplex Communication with WCF Named Pipes.
Sample
Here is a sample I'm writing, this sample serves for creating a communication route for several processes running on the same machine. (Sample can only downloaded from CodeProject page)
In the sample, it contains two client WPF application (Client1 and Client2), one host
WPF application (WpfWcfNamedPipeBinding). Clients can send message to host, in return host can also send message to clients. Client1 can also send messages to Client2 through the host.
——————————-During the development, here are Some questions i didn’t understand ————-
- I cannot create a namedPipeBinding Duplex Service Reference. The Client test WCF says that the Metadata contains a reference cannot be resolved. Once i removed
CallbackContract
from the service contract, it works good. So i had to create my own client proxy instead which of visual studio. (SessionMode.Required
,
CallbackContract
, and InstanceContextMode.PerSession
)
————————————————————————————————————————————-
Create a Wcf service
library Write a communication contract for this service.
I’d like to have a duplex contract, because it’s more practice. For example, we have a server. Client1 request some data from server. The callback function will be called from the server after client requested further information and the server had enough time to retrieve them.
MSDN : A duplex service, therefore, can send messages back to the client endpoint, providing event-like behavior. Duplex communication occurs when a client connects to a service and provides the service with a channel on which the service can send messages back to the client. Note that the event-like behavior of duplex services only works within a session.To create Duplex contract you need to create a pair of interfaces, a service contract and a callback contract. The callback contract is the interface that defines the operations that the service can call on the client endpoint. A duplex contract does not require a session, although the system-provided duplex bindings make use of them. To define a callback contract, the
ServiceContract
attribute offers the CallbackContract
property of the type Type.
[ServiceContract(SessionMode = SessionMode.Required, CallbackContract = typeof(INamedPipeBindingCallbackService))]
public interface INamedPipeBindingService
[ServiceContract]
public interface INamedPipeBindingCallbackService
In this example, the service uses the PerSession instance mode to maintain the result for each session. Because server also need to send message to client without client’s request, server need to maintain the connect session of client. And this is set in the implementation of service contract :
[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerSession)]
public class NamedPipeBindingService : INamedPipeBindingService
Below is the fully service contract interface defined in this sample. It contains a service contract and a callback service contract. In the OperationContract attribute, i set IsOneWay = true, because the service may want to invoke the callback reference that’s passed in during the execution of a contract operation. How ever such invocations are disallowed by defaut. By defaut, the service class is configured for single-threaded access: the service instance context is associated with a lock, and only one thread at a time can own the lock and access the service instance inside that context. Calling out to the client during an operation call requires blocking the service thread and invoking the callback. The problem is that processing the reply message from the client on the same channel once the callback returns requires reentering the same context and negotiating ownership of the same lock, which will result in a deadlock. Note that the service may still invoke callbacks to other clients or call other services; it is the callback to its calling client that will cause the deadlock. Set IsOneWay = true is method to avoid deadlock. Instead of two other method, set IsOneWay = true can maintain the service as single-threaded without release the lock. Set IsOneWay = true enables the service to call back even when the concurrency mode is set to single-threaded, because there will not be any reply message to contend for the lock.
[ServiceContract(SessionMode = SessionMode.Required, CallbackContract = typeof(INamedPipeBindingCallbackService))]
public interface INamedPipeBindingService
{
[OperationContract(IsOneWay = true)]
void Message(CommunicatUnit composite);
#region Callback Connection Management
[OperationContract(IsOneWay = true)]
void Connect();
[OperationContract(IsOneWay = true)]
void Disconnect();
#endregion
}
[ServiceContract]
public interface INamedPipeBindingCallbackService
{
[OperationContract(IsOneWay = true)]
void Message(CommunicatUnit composite);
}
In the service contract, there is a region "Callback Connection Management". It is because of mechanism of callback supplied by WCF, we have to come up with ourself some application-level protocol or a consistent parttern for managing the life cycle of the connection. The service can only call back to the client if the client-side channel is still open, which is typically achieved by not closing the proxy. Keeping the proxy open will also prevent the callback object from
being garbage-collected. You may always want to add the Connect()
and
Disconnect()
pair on a sessionful service simply as feature, because it enables the client to decide when to start or stop receiving callbacks during the session.
If the services maintains a reference on a callback endpoint and the client-side proxy is closed or the client application itself is gone, when the service invokes the callback it will get an
ObjectDisposedException
from the service channel. It is therefore preferable for the client to inform the service when it no longer wishes to receive callbacks or when the client application is shutting down. In
NamedPipeBindingService
, InstanceContextMode = PerSession
, read comments written on NamedPipeBindingService
to get more info.
After the creation of service contract, it turns to implement contracts. You could find implementation of contract in the sample's source code. For complete namedPipeBinding service part, all we need to do left is just define service endpoints.
Service Endpoint, this is always an obstacle for my WCF study, and this time i decided to read all pages about Endpoints from book of Juval Löwy.
- Every end point must have three elements Address, Contract and Binding, and the host exposes the endpoint. Logically, the endpoint is the service's interface and is analogous to a CLR or COM interface.
- All endpoints on a service have unique address, and a single service can expose multiple endpoints.
- Use fully qualified type names for
specifying the service and the contract type.
- If the endpoint provides a base address, that address scheme must be consistent with the binding, such as HTTP with WSHttpBinding
- Administrative configuration (App.config) provides the flexibility to change the service address, binding, and even exposed contracts without rebuilding and redeploying the service.
- Use
bindingConfiguration
to enable transaction propagation. The default binding implicitly configures all endpoints that don't
explicitly reference a binding configuration. Only on default binding configuration per binding type. - For IPC namedPipedBinding, it seems that we should not define a port like "net.tcp//localhost:10001/"
Metadata Exchange endpoint. Two options for publishing a service's metadata : Metadata over HTTP-GET and use a dedicated endpoint. Publishing metadata over HTTP-GET is merely a WCF feature, there are no guarantees that other platforms you interact with will support it. Publishing the metadata over a special endpint is a better way.
WCF provides dedicated binding transport elements for the HTTP, HTTPS, TCP, and IPC protocols. There is no need to enable the HTTP-GET option. The host implement the MEX endpoint for your service is include the serviceMetadata tag in the behavior.
<system.serviceModel>
<bindings>
<netNamedPipeBinding>
<binding name="NamedPipeBinding_INamedPipeBindingService" closeTimeout="00:05:00"
openTimeout="00:20:00" receiveTimeout="00:20:00" sendTimeout="00:20:00"
transactionProtocol="OleTransactions" hostNameComparisonMode="StrongWildcard"
maxConnections="10" maxBufferPoolSize="50000000"
maxBufferSize="50000000" maxReceivedMessageSize="50000000">
<readerQuotas maxDepth="32" maxStringContentLength="50000000"
maxArrayLength="50000000" maxBytesPerRead="50000000"
maxNameTableCharCount="50000000" />
<security mode="Transport" />
</binding>
</netNamedPipeBinding>
</bindings>
<services>
<service name="WcfServiceLibraryNamedPipe.NamedPipeBindingService" behaviorConfiguration="MEX">
<endpoint address="net.pipe://localhost/WcfServiceLibraryNamedPipe
/NamedPipeBindingService" binding="netNamedPipeBinding"
bindingConfiguration="NamedPipeBinding_INamedPipeBindingService"
contract="WcfServiceLibraryNamedPipe.INamedPipeBindingService" />
<endpoint address="net.pipe://localhost/WcfServiceLibraryNamedPipe/
NamedPipeBindingService/mex"
binding="mexNamedPipeBinding" contract="IMetadataExchange" />
</service>
</services>
<behaviors>
<serviceBehaviors>
<behavior name="MEX">
<serviceMetadata httpGetEnabled="False" />
</behavior>
</serviceBehaviors>
</behaviors>
</system.serviceModel>
Host
Once service contract and callback contract are defined, we can create a host for making this named pipe service work. Here is nothing special than others, this sample use ServiceHost class for create and maintain the host service. Just one thing, this sample recreated the implementation of service contract in the project of host, so please review the Administrative configuration. In the host's implementation of service contract, we still set InstanceContextMode = PerSession (like
mentioned above, it specifies the lifetime of the InstanceContext
object, WCF can create a new InstanceContext object for every session, InstanceContext manages the association between the channel and the user-defined service objects).
In the implementation of contract service, it records every client's callback channel reference. Host uses these callback channel references to resend messages to clients.
Client
Instead of generating proxy with visual stuido, my sample create its own client proxy. Because i didn't
successfully added service reference in my client project. This sample uses
DuplexChannelFactory
to connect to host. Client1 in the sample uses the implementation of callback service contract to be the first parameter of constructor of
DuplexChannelFactory
, client2 in the sample uses InstanceContext
to be the parameter.
It alse need to define endpoint name for creating the duplex channel between clients and host. But it is easier than host side. We just need to add client endpoint to let the program find its channel's enter.
<system.serviceModel>
<client>
<endpoint address="net.pipe://localhost/WpfWcfNamedPipeBinding/NamedPipeBindingService"
binding="netNamedPipeBinding"
contract="WcfServiceLibraryNamedPipe.INamedPipeBindingService"
name="NamedPipeBindingServiceEndpoint" />
</client>
</system.serviceModel>
Once the basic acknowledge is clearly understood, the left work is just easily. Please review the code source of sample to understand more.
Points of Interest
I didn't achieve processes inter communication with a service reference, because i encountered problem. But i use another method to realize WCF duplex namedPipeBinding communication between processes.
This article didn't concentrate to present my sample, instead of it this article talked more about basic WCF acknowledge. We can write a WCF
application easily with samples in CodeProject. But we should be responsible for our project!