In this chapter, we showed you how to build a simple distributed application by using .NET Remoting. We covered each of the major tasks that you need to perform for any distributed application that uses .NET Remoting. By now, you should have a good understanding of how to take advantage of the .NET Remoting infrastructure in your application development. The remainder of this book will show you how to take advantage of the more advanced .NET Remoting features by developing a custom proxy, channel, and formatter.
SOAP and Message Flows
By now, you should be well on your way to understanding how to use .NET Remoting to develop distributed applications. To further your understanding, this chapter will look at the actual messages exchanged among the objects within the JobClient and JobServer applications. Examining these messages will give you insight into the kind of information that .NET Remoting exchanges among distributed objects. However, before we look at these messages, we need to briefly discuss SOAP. If you’re already familiar with SOAP, feel free to skip ahead to the “Message Flows” section of the chapter.
Simple Object Access Protocol
Much of the .NET Framework’s interoperability hinges on SOAP. Although developers can choose more efficient protocols, none of these protocols has SOAP’s flexibility. So what exactly is SOAP?
In broad terms, SOAP is an XML-based protocol that specifies a mechanism by which distributed applications can exchange both structured and typed information in a platform-independent and language-independent manner. In addition, SOAP specifies a mechanism for expressing remote procedure calls (RPC) as SOAP messages. SOAP specifies an envelope format for XML data wrapping, simple addressing, as well as data encoding and type encoding. This data-encoding scheme is especially interesting because it standardizes type descriptions independent of platform or programming language. Although many people assume that SOAP relies on HTTP for its transport, SOAP can theoretically use any transport, such as Simple Mail Transfer Protocol (SMTP) or Microsoft Message Queuing (MSMQ). In fact, you could put a SOAP message in a file on a floppy disk and carry it to a server for processing. You can find the complete specification for SOAP 1.1 at http://www.w3.org/TR/SOAP/.
SOAP has made such a big impact on the industry for many reasons, including the following:
SOAP is simple. Because SOAP leverages existing technologies such as HTTP and XML, implementing SOAP is easy.
SOAP is ubiquitous. Because it’s simple to implement, it’s also widely available. At the time of this writing, more than 30 SOAP implementations are available for a variety of programming languages and platforms.
SOAP is built for the Internet. The SOAP RPC specification deals with the necessary HTTP headers for transporting SOAP messages. Because they integrate tightly with HTTP, SOAP messages are firewall friendly and easily supported by existing systems. SOAP also describes a simpler message-based scheme, but we’ll discuss only the more full-featured SOAP RPC because this is what .NET Remoting uses.
One of SOAP’s biggest advantages is also its biggest disadvantage. Because it’s text based, SOAP is easy to read and is portable. However, converting data structures into verbose tag descriptions takes processing time and results in a larger payload. Still, the extra processing time is fairly minimal and shouldn’t be an issue if your application needs the benefits that SOAP provides.
Why Should We Care About SOAP?
These days, most developers don’t write SOAP directly. SOAP has become such a standard protocol that it’s now part of the plumbing and vendors provide toolkits for its use. This is exactly what the .NET Framework gives developers. With .NET, developers can write .NET Remoting applications and choose SoapFormatter via configuration, write XML Web Services that naturally use SOAP, or use the XmlSerializer class to serialize native classes into SOAP messages.
So if the .NET Framework hides the SOAP details from us, why should we care about them? One of the best reasons is that examining these details allows us to peek beneath the covers of .NET Remoting application communications. In addition to all SOAP’s benefits (such as being firewall friendly and platform independent), SOAP is text based, unencrypted, and therefore human readable. If you configure your .NET Remoting application to use SoapFormatter, you can spy on the traffic between client and server and learn a lot about the way .NET Remoting works.
In its simplest form, the SOAP specification defines an XML schema for SOAP messages. But the SOAP specification goes further: it defines the headers for using HTTP as a transport mechanism. We’re interested only in the HTTP transport binding of SOAP in this book. Therefore, we’ll start with an example of a standard HTTP header for a SOAP message:
HTTP/1.1 /JobServer/JobURI POST Content-Type: text/xml SOAPAction: MyMethod
The HTTP Uniform Resource Identifier (URI) describes the RPC endpoint—in this case, /JobServer/JobURI. The Content-Type header of text/xml,which is followed by a SOAPAction header, defines the SOAP header. Based on the SOAPAction value of MyMethod, we can presume that this HTTP request includes a SOAP message containing information to allow the recipient to invoke the MyMethod method on the object associated with /JobServer/JobURI. In the “Message Flows” section of this chapter, you’ll see that .NET Remoting uses the SOAPAction header to identify the name of the method to invoke.
RPC-based SOAP uses a request/response pattern. A client sends a one-way request message, and the server responds by creating a one-way response message. This pattern fits perfectly with HTTP’s request/response scheme.
SOAP Message Elements
The fundamental unit of exchange SOAP defines is the SOAP message. The following template shows the order and nesting of the elements that comprise a SOAP message. An instance of this template directly follows the HTTP header we just described.
<SOAP-ENV: Envelope> <SOAP-ENV: Header> ... Header information (the Header element may be included) </SOAP-ENV: Header> <SOAP-ENV: Body> ... Body information (the Body element must be included) </SOAP-ENV: Body> </SOAP-NEV: Envelope>
The SOAP Envelope
The <Envelope> element is the mandatory root of every SOAP message. In addition, every <Envelope> has the following characteristics:
It must reference the SOAP Envelope namespace (xmlns: SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/").
It may contain a single <Header> element.
It must contain exactly one <Body> element.
The full SOAP <Envelope> element produced by .NET Remoting typically looks like this:
<SOAP-ENV:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/" xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:clr="http://schemas.microsoft.com/soap/encoding/clr/1.0" SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
Plenty of namespace identifiers are included in this code to scope various elements of the SOAP message. The first two attributes map the conventional alias identifiers xsi and xsd to the XML namepsaces http://www.w3.org/2001/XMLSchema-instance and http://www.w3.org/2001/XMLSchema, respectively. The xmlns:SOAP-ENC attribute maps the SOAP-ENC alias identifier to the SOAP 1.1 encoding schema. The xmlns:SOAP-ENV attribute maps the SOAP-ENV alias identifier to the SOAP 1.1 envelope schema. Later in this section, you’ll see that most elements in the SOAP message use the namespace alias identifier SOAP-ENV for the elements and attributes defined in the <Envelope> element. Also note that the encodingStyle attribute indicates that the SOAP message follows the encoding rules specified in Section 5 of the SOAP 1.1 specification.
The Soap Header
If present, the <Header> element must directly follow the opening <Envelope> tag and appear, at most, once. Header entries are child elements of the <Header> element and provide a mechanism for extending the SOAP message with application-specific information that can affect the processing of the message. Furthermore, a header entry can include header attributes that affect its interpretation. The SOAP 1.1 specification defines two header attributes: actor and mustUnderstand. Keep in mind that the SOAP message can travel along a chain of SOAP message processors en route to its destination. The actor attribute specifies which of these message processors should actually act upon the message. The mustUnderstand attribute indicates whether the recipient of the message absolutely must know how to interpret the header entry. If mustUnderstand has a value of 1 rather than 0, the recipient must understand the header entry or reject the message. The following code snippet shows a header entry element named MessageID that contains the mustUnderstand attribute:
<SOAP-ENV:Header> <z:MessageID xmlns:a="My Namespace URI" SOAP-ENV:mustUnderstand="1"> "2EE0E496-73B7-48b4-87A6-2CB2C8D9DBDE" </z:MessageID> </SOAP-ENV:Header>
In our example, the recipient must understand the MessageID header entry to process the message.
The SOAP Body
Exactly one <Body> element must exist within an <Envelope> element. The <Body> element contains the actual payload destined for the endpoint. This is where the interesting application-specific data is located. In .NET Remoting, the <Body> element contains method calls with parameters including XML versions of complex data types such as structures. Section 5 of the SOAP 1.1 specification describes how to serialize arrays, structures, and object graphs; many developers colloquially refer to this encoding scheme as Section 5 encoding. Here’s an example of a typical SOAP <Body> element used for RPC:
<SOAP-ENV:Body> <myns:GetPopulationOfState xmlns:myns="my-namespace-uri"> <state>Florida</state> </myns:GetPopulationOfState> </SOAP-ENV:Body>
Child elements of the <Body> element’s method name element will contain any input and will reference parameters of the method. In the example, the child element <state> specifies that the caller wants to retrieve the population for the state of Florida. A response message payload will then contain any output and will reference parameters for the method. The recipient of the GetPopulationOfState request message might respond with the following SOAP message:
<SOAP-ENV:Envelope xmlns:SOAP-ENV=http://schemas.xmlsoap.org/soap/envelope/ SOAP-ENV:encodingStyle= "http://schemas.xmlsoap.org/soap/encoding/"/> <SOAP-ENV:Body> <myns:GetPopulationOfStateResponse xmlns:myns="my-namespace-uri"> <Population>15982378</Population> </myns:GetLastTradePriceResponse> </SOAP-ENV:Body> </SOAP-ENV:Envelope>
The SOAP Fault
If an error occurs on the server, the SOAP specification defines a <Fault> tag that must be a child of the <Body> element. The <Fault> element carries information describing why the operation failed on the server. Of course, the implication is that you’ll see <Fault> tags in response messages only.
Before we jump into some examples of SOAP message flows for the sample applications we developed in Chapter 3, “Building Distributed Applications with .NET Remoting,” it’s worth noting that another “flavor” of SOAP exists. We’ve already described the RPC/encoded form of SOAP that .NET Remoting uses. The other form of SOAP, which is known as document/literal, is the default SOAP style used by ASP.NET XML Web Services.
Document/literal SOAP messaging has no rules about what the <Body> element can contain. This is a more flexible scheme because the content can be anything agreed upon by the sender and receiver. Document/literal messaging serializes data based on XML schemas that handle data as XML rather than as objects or structures. Naturally, the two camps of SOAP messaging are waging a religious war. We’ll stay out of that one and focus on RPC/encoded SOAP so that we can spy on .NET Remoting message flows.
Nothing in the SOAP specification says that RPC must be paired with encoded serialization and that document messaging must be paired with literal serialization. However, nearly all existing SOAP stacks implement the specification this way.