became a last minute entry to do the /spWSE article as a presentation at MobileDevCon. don't want to disappoint, so i want to have some new tricks to show, in case anybody actually comes to my presentation [switching tenses, because i am writing this article before the conference, but will not be releasing it until afterwards]. my 1st choice was to pick a new spec and implement it for the .NET Compact Framework (CF). there are just smack loads of WS-* specifications out there to choose from, even if you dont count the duplicate ones that are duking it out in the industry. thought that WS-ReliableMessaging (RM) made perfect sense, since these devices are mobile and dont have the most reliable connections. e.g. driving through a tunnel with your Smartphone, or switching from one cell tower to another, or moving from WiFi to a bluetooth network connection. all-in-all, lots of scenarios for the connection to fail and cause a bad user experience. that is where RM fits in.
the first thing i did was look for the specification. don't confuse it with WS-Reliability. WS-Reliability is a similar specification being pushed as well, but is not the MS-backed flavor. MS is behind WS-ReliableMessaging, so i am targeting that one as the most likely to make it into the WSE bits (rumor has it v3, although v2 is not quite golden yet). found the WS-ReliableMessaging specification and saw that it was published March 2003. over a year ago, so i was sure there would be a server-side implementation out there that i could call from the CF. to my complete and utter surprise, the only implementation is in the Indigo alpha bits on the Longhorn alpha. you've got to be kidding me. a year old spec ... and nothing ... come on people, the economy is not that bad. almost as silly as Speech .NET being in beta(s) for over a year and a half. so i started reading the spec, and doing a simple design for both the server side and client side pieces.
RM is mostly client driven through SoapHeaders. the client sends multiple requests providing a Sequence identifier. if the server receives the messages out of order, then it can order them. when the client is done, it sends a LastMessage node in the Sequence. the server responds with a header called SequenceAcknowledgement. it contains node(s) specifying the Range of messages received. if all the messages were received, then the server would execute the logic, and let the client know of the success. if the server does not have all the messages, it will not execute its logic, and return a SequenceAcknowledgment showing the ranges received. the client will then determine the messages that were not received, and can resend them. it can also ask for another SequenceAcknowledgement from the server by sending an AckRequested header. the specification has a sequence diagram, as well as sample messages detailing this scenario.
the specification also has the schema for the messaging. about half of it involves the actual SoapHeaders and their children. the other half is dedicated to expressing the structure of Policy elements. NOTE i'm not implementing the Policy pieces for this. usually i write the SoapHeaders from scratch, but this time i decided to use the XSD tool. a little fumbling around, and it generated the proper classes with this command:
xsd.exe /c rm.xsd utility.xsd
rm.xsd is the schema for ReliableMessaging. utility.xsd is from WS-Utility, which RM builds upon. about the only changes i had to do was make the SoapHeader elements inherit from System.Web.Services.Protocols.SoapHeader. this included SequenceType, SequenceAcknowledgment, and AckRequestedType. thinking ahead, i also ripped out the XmlAnyElement and XmlAnyAttributes because i knew they would cause problems for CF.
rm.xsd / utility.xsd / rm_utility.cs
the next order of business was to tie the SoapHeaders into a WebService, and add the appropriate logic. if i was really interested in getting the server-side piece just right, i would have implemented it as a SoapExtension or as a WSE Filter. instead, i just wanted to get it working so much as to support client calls, to this end, i just threw the RM logic into the actual WS. sorry, but i already explained how pissed i was for having to write this in the 1st place :) the business logic of the web service itself is nothing. the WebMethod is Ping(), accepts no arguments, and returns a bool. the bool is False when the sequence is not complete, and True when an entire RM sequence has been received from the client. the RM logic is pretty simple and it is looking for Sequence and AckRequested SoapHeaders. internally it keeps a collection of Sequence/Identifiers and each of those has a collection of Sequence/MessageIdentifiers. finally, it keeps state as to whether or not the LastMessage element has been sent. when LastMessage is sent, it determines if the Sequence is complete. if it is, it returns True as the SoapBody response, and a SequenceAcknowledgement representing that all messages have been received. if not, it returns a SequenceAcknowledgement showing what Range of messages has been received. if an AckRequested is received, then it returns a SequenceAcknowledgement with the current range. also, it implements portions of the expected SoapFault messages. here is the ASMX
created the simplest WinForm to test the WebService, before porting to CF. this screen cap shows that is just send the 4th message as the final message in the sequence. the MessageBox to the side shows that the web service did not execute the business logic, because it is missing the 3rd message. upon receiving this response SoapHeader, the client would resend the 3rd message, and the web service would execute.
the client just blindly sends requests as it always does, only adding the sequence and message identifiers. an additional level of abstraction is added before the message is actually sent, as an object holding the state of the request. it is all the info that is needed to recreate and send the request again, in case it were to fail. then this object is added to a Hashtable using the message id as the key. when the client sends the last message, it reads the SequenceAcknowledgement. it parses it, and if one of the messages it sent previously was not received by the server, then it retrieves that object from the Hashtable, and then resends that message. then it checks that SequenceAcknowledgement to determine if it needs to continue resending.
trace messages follow. request 1 is sent and received by the server. request 2 is sent and not received. request 3 is then sent as the LastMessage in the Sequence. the server responds with a SequenceAcknowledgement showing that it received both 1 and 3. the client determines that message 2 was not received, and resends it along with an AckRequested element. the server then responds with a SequenceAcknowledgement that all messages were received.
req1.xml / req2.xml (not received) / req3.xml / res3.xml / req4.xml (resend of 2) / res4.xml
added a little bit more logic for handling asynch communications as well. with this, i can have my PPC connected with active synch. then it can start sending some messages from a Sequence to the web service. next, i can undock the device and it loses its connection entirely. with no connection, i can send a couple more messages from the Sequence. redocking the device, to get a connection, i can send the final message of the Sequence. upon receiving the final message, the web service returns the SequenceAcknowledgement showing what was received; and then the client can re-send the messages that were not. slick
here is a video clip of it working (2 megs)
WS-ReliableMessaging is a pretty simple specification that is greatly needed for mobile devices. with minimal effort, was able to create an ad hoc web service and device client to support the protocol.
3.9.2004 the WS-ReliableMessaging spec was updated to support 'Explicit Sequence Creation' and 'Negative Acknowledgements'. it also has a more human readable article . i have not incorporated these changes
i might revisit the client-side portion of this to get it to work with an MS provided server-side implementation.
still waiting to beat on Whidbey.