Channels are objects that transport messages between applications across remoting boundaries, whether between application domains, processes, or computers. A channel can listen on an endpoint for inbound messages, send outbound messages to another endpoint, or both. This enables you to plug in a wide range of protocols, even if the common language runtime is not at the other end of the channel.
Channels must implement the IChannel interface, which provides informational properties such as and . Channels designed to listen for a particular protocol on a particular port implement IChannelReceiver and channels designed to send information implement IChannelSender. Both the and objects implement both of these interfaces, so they can be used to send or receive information.
You can register channels with the remoting infrastructure in the following ways:
If you are publishing a remotable object, callprior to registering your server object.
If you are consuming a remotable object's functionality, call RegisterChannel prior to creating an instance of your server object.
Channels can also be loaded from the remoting configuration file. For details, see Configuration.
On the client side, messages are passed to the client channel sink chain after they traverse the client context chain. The first channel sink is typically a formatter sink; it serializes the message into a stream, which is then passed down the channel sink chain to the client transport sink. The client transport sink then writes this stream out to the wire.
On the server side, the server transport sink reads requests from the wire and passes the request stream to the server channel sink chain. The server formatter sink at the end of this chain deserializes the request into a message. It then passes this message to the remoting infrastructure. For more information about channel sinks, see Sinks and Sink Chains.
When a client calls a method on a remote object, the parameters and other details related to the call are transported through the channel to the remote object. Any results from the call are returned in the same way. A client can select any of the channels registered on the server to communicate with the remote object, thereby allowing developers the freedom to select the channels that best suit their needs. It is also possible to customize any existing channel or build new ones that use a different communication protocol. Channel selection is subject to the following rules:
At least one channel must be registered with the remoting system on the server before a remote object can be called.
Channels must be registered before objects are registered. If a channel is not registered on the client, the remoting system chooses or creates one to send outbound calls.
If the client expects a callback function, a listening channel must be registered on the client and the server must be configured to use a compatible channel.
Channels are registered on a per-application-domain basis. A single process can contain multiple application domains. When a process ends, all channels registered by it are automatically destroyed.
Channel names must be unique within an application domain. For example, because the default channels have names, to register two HttpChannel objects in one application domain, you must change the names of the channels before registering them. The following C# code example demonstrates this.
IDictionary prop = new Hashtable(); prop["name"] = "http1"; prop["port"] = "9001"; ChannelServices.RegisterChannel(new HttpChannel(prop, null, null));
You cannot register a channel that listens on a specific port more than once. Even though channels are registered on a per-application-domain basis, different application domains on the same machine cannot register the same channel listening on the same port.
If you are not sure about whether a port is available, use 0 (zero) when configuring your channel's port and the remoting system chooses an available port for you.
Clients can communicate with a remote object using any registered channel. The remoting system ensures that the remote object is connected to the right channel when a client attempts to connect to the object. The client is responsible for calling ChannelServices.RegisterChannel before attempting to communicate with a remote object. If it expects a callback function, the client must register a channel and a port.
When a client calls a method on a proxy, the call is intercepted, bundled into a message, and passed to an instance of the RealProxy class. The class forwards the message to the message sink for processing. A message sink establishes a connection with the channel registered by the remote object and dispatches the message over the channel into the originating application domain. There, the message is unmarshaled and the call is made on the remote object itself.
When remoting initializes a proxy to a remote object in the client's domain, a message sink capable of communicating with the remote object is retrieved from the channel configured by the client by calling IChannelSender.CreateMessageSink on the selected channel.
One confusing aspect of the remoting system is the relationship between remote objects and channels. For example, how a WellKnownObjectMode.SingleCall remote object listen for clients to connect, if the object is activated only when a call arrives.
This is possible in part because remote objects share channels; a remote object does not own a channel. Server applications that host remote objects must register the channels they require as well as the objects they expose with the remoting system. When a channel is registered, it automatically starts listening for client requests at the specified port. In the case of synchronous calls, the connection from the client is maintained for the duration of the message call. Since each client connection is handled in its own thread, a single channel can service multiple clients simultaneously.