A channel typically relates to one aspect of the messaging functionality in an application. If a WCF application is secure, reliable, and transactionally capable, that application will use one channel for security, another for reliability, and another for transactional capability. Because each channel has a discrete set of functionality and most applications need more functionality than one channel can provide, WCF applications arrange channels in a stack and leverage the functionality across the stack. Very seldom does a production application use a channel in isolation.
A WCF application references the topmost channel in the stack only. When stacked, a channel in the stack is responsible for doing some work and invoking the next channel in the stack (or invoking the next channel and then doing its work when the previous call returns). The important point here is that once a message is sent to the channel stack, the channel stack itself pulls or pushes messages through the stack. There is no outside engine that manages the transition of a Message from one channel in the stack to the next. Once an application builds a channel stack, the channel stack is an opaque entity. As you’ll see later in this chapter, it is possible to query the channel stack for certain capabilities, but this is a far cry from the full transparency one might expect when first approaching a topic as important as channels.
When channels are arranged in a stack, the composition of the stack dictates many of the features of the application, and each channel in the stack has a distinct role in the overall functionality of the application. For the most part, channel stacks accept or return a Message at the topmost channel in the stack, and the channel at the bottom of the stack emits or receives bytes at the transport level. Channel stacks on a sending application accept a message at the top of the stack and emit bytes at the bottom of the stack. Channel stacks on a receiving application, on the other hand, accept bytes at the bottom of the channel stack and return a Message at the top of the stack. What happens in the middle of the stack depends on the channels residing there. Typically, the channels in the middle of a channel stack are the physical implementations of a WS-* protocol or security toll gates. Figure 6-1 illustrates the composition of a typical channel stack on a sending application.
Figure 6-1: A typical channel stack
Notice that the bottom channel in the stack accepts a Message as input and outputs bytes on the wire. This bottom channel in the stack is also responsible for the mechanics of communication on a particular transport. If the transport is TCP, this channel is responsible for the socket connection and sending bytes to that socket. If the transport is MSMQ, the bottom channel is responsible for connecting to an MSMQ queue and sending the message to that queue. Notice also in Figure 6-1 that the channels arranged above the transport channel have distinct roles in message processing (for example, transactions, security, and reliability).
There is no concrete, one-size-fits-all channel type definition. The WCF type system abounds with channel type definitions, and each channel type definition results in a channel object with a stated purpose. For example, all supported transports in WCF have at least one channel type definition in the WCF type system that provides a WCF application the physical means to communicate over that transport. Likewise, the WCF type system contains many channel definitions that are the physical means of providing the venerated features of reliability, transactional processing, and security.
Factory objects instantiate channel objects. In most cases, there is a one-to-one correlation between factory objects and channel objects. In other words, each channel type has a corresponding factory type. Just as there is no one-size-fits-all channel type, there is no one-size-fitsall factory type. Because channels are frequently arranged in a stack at run time, the factory objects that create the channel stack are also frequently arranged in a stack. In one sense, the arrangement of factory objects in the factory object stack dictates the arrangement of the channels in the channel stack. You’ll learn more about the channel factory members in Chapter 7, “Channel Managers.” For now, it is enough to know that channels are not directly instantiated by user code, but rather through a channel factory.