Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Languages / C#

WCF Duplex, SMS, a WebServer and a Windows Client (and a couple of other things)

4.88/5 (5 votes)
15 Jun 2009CPOL11 min read 33.4K   867  
Sending and receiving text messages from a Windows client

Introduction

This example demonstrates WCF duplex communications between a Windows client and a web server that listens for HTTP posts from the (free) Zeep SMS text messaging service.

In other words, it's an application that allows a Windows client to post text messages to a subscriber's cell phone, and receive text messages posted from a subscriber's cell phone.

Six Main Features

  1. The mobile_settings web site/page is used to subscribe new users and is where the whole thing begins.
  2. The SMSClient sends and receives text messages to and from users who have subscribed to the service.
  3. The SMSWebServer acts as a call back URL that Zeep sends HTTP posts to whenever a new subscriber signs up, or whenever a current subscriber sends a text message from their phone.
  4. The MessageService sends subscriber's text messages from the SMSWebServer to the SMSClient.
  5. The SMSService is used to send text messages from the Windows SMSClient to a subscriber via Zeep.
  6. The ZeepmobileSMS.dll posts messages to Zeep and is used by the SMSService.

Getting Started

To get things started, the first thing you need to do is visit Zeepmobile.com and get an account. While you're there, create a test phone. Zeep will also assign to you an api_key and a secret key. You'll need to attach the api_key to the URL that gets submitted to Zeep during the new subscriber subscription process. Look for the URL in the default.aspx.cs page inside of the MobileSettings project provided in this example. Replace the x's with your api_key.

You will need to attach your assigned api_key and secret key to the public static string constants found in the source file called ZeepMobile.cs in the ZeepMobileSMS project which is also provided in this example.

While creating your account with Zeep, you'll need to come up with a SMS Prefix. When you send a text message, be it from your cell phone or the Zeep test phone, will need to enter the SMS Prefix as the first line of your message. All messages for all subscribers are sent to 88147. Zeep uses the SMS Prefix to figure out where to send the message.

When you use the Zeep test phone for the first message, you won't be able to cursor down to the next line so just type the prefix, a space, and then your message. Also, occasionally you'll have to reload the test phone in order to see messages sent from the Windows client. Just right click on the test phone and select reload. One more thing: I was unable to use Internet Explorer in order to view my Zeep test phone. Internet Explorer was throwing an error whenever I clicked Zeep's view test phone link so I'm using Firefox while surfing their site.

Publish the 2 WCF web services and the Zeep registration web site (mobile_settings) to your web server. Before you do, you will need to change any references to "my.domain.name" to your own domain name.

  1. Go into the web.config file for the MessageService and change the endpoint address (and dns property) to point to your domain.
  2. Go into the IMessage.cs file inside of the MessageService project and change the line "Namespace = http://your.domain.name" to whatever domain name you are using.
  3. Then go to the service reference for both the SMSClient and the SMSWebServer and update the service references. Check the app.config files for both to be sure that the endpoint identity for servicePrincipleName is there, and that the clientBaseAddress for the binding also exists.

My Infrastructure

infrastructure.jpg

The Windows Client

windowsclient.jpg

The Windows client uses basicHttpBinding for sending text messages to a subscriber and wsDualHttpBinding to receive message sent from a subscriber.

The most important thing I found here was that without the...

ASP.NET
clientBaseAddress=http://localhost:8000/

... property on the wsDualHttpBinding AND the...

ASP.NET
serviceprincipalname value="SMSMessageService"

... property on the endpoint/identity, well it just won't work. It appears from my testing that it really doesn't matter what the value of the servicePrincipalName is, as long as it has something. The same is true for what follows after the :8000/ (if anything) on the clientBaseAddress.

The SMSService

The SMSService is responsible for accepting messages from the client and forwarding them along to Zeep (which in turn forwards them along to your subscriber's phone).

Client --> SMSService --> Zeep --> Subscriber

The MessageService

The MessageService pushes out text messages sent from a subscriber's phone to the Windows client.

Subscriber --> Zeep --> SMSWebServer --> MessageService --> Client

One of the cool things about the MessageService is that it includes a ServiceHost and a ServiceHostFactory. The ServiceHostFactory has the ability to monitor for the serviceHost_Closing event. This happens when you shut down IIS. When that event fires, it sends a message to all clients, notifying them that IIS has shut down and that the clients need to close. The SMSWebServer however, is not shutdown when IIS is. It continues to monitor for inbound text messages from subscribers, but it will notify them that there are no clients online to receive their text messages when IIS is down.

The SMSWebServer

The SMSWebServer is a stand alone web server, i.e., one that is not hosted inside of IIS and it acts as the callback URL that Zeep posts HTTP messages to. So when you setup your Zeep account, you'll need to update the Zeep callback URL with your address.

For instance: (http://your.domain.name:port).

You'll need to go into the SMSWebServer.cs file and update the line with the _server.Start(9999) and replace the 9s with the port that you'll want the server to listen on.

I built the SMSWebServer as a stand alone server for a couple of reasons. My first attempt at building a Zeep callback was one where I thought I'd simply use a *.aspx page, but for some reason, Zeep didn't seem to like it. Even after I finally figured out how to strip out all of the unnecessary response headers that IIS typically sends (and Zeep didn't want returned), for some reason Zeep was still throwing an error. I've got a question about it on the Zeep message board, but as of this writing, I haven't gotten an answer, so I went ahead with this solution. It should also be noted that as of this writing, Zeep is still in beta so I'm not pressuring them for an answer.

I also wanted to provide an example on how to update a UI from within a static event. So I embedded the XF.Server in a Windows Form and simply put one text box on the form. Whenever a new message comes through or an error occurs, I update the text box from the static event. I also like the fact that this isn't a console application and I wrote it so that when it is minimized, it shrinks down to the notification area of the tool bar. The Windows Form (rather than a console app) simply provides much more control over events when closing and exiting.

The SMSWebServer sends "keep-alive" messages to the clients every five minutes via the MessageService in order to keep the clients from timing out. However, the clients don't send "keep-alive" messages. That reminded me too much of polling, and it was something I was trying to avoid.

You may notice the use of the AsyncObject. I'm really not using it for anything other than a "placeholder" (if you will) for the InstanceContext Implementation Object. I needed something for inside of the static OnReadComplete async event in order to send the message to the client.

You may be familiar with something that looks like the following:

C#
_messageService = new MessageServiceClient(new InstanceContext(this), 
					"WSDualHttpBinding_MessageService");

That works fine and dandy when you are using it inside of a private event, but inside of a static event, the "this" context is unavailable.

So what I did was to define the Async object outside of the static event once like this:

C#
internal static AsyncObject _ao = new AsyncObject();

internal static InstanceContext _instanceContext = new InstanceContext(_ao);

... and then used it like this inside the static OnReadComplete event:

C#
MessageServiceClient messageService = null;

messageService = new MessageServiceClient
		(_instanceContext, "WSDualHttpBinding_MessageService");

Subscribing a New Subscriber and Sending Messages

After you have everything configured, setup and compiled, you will need to subscribe a new subscriber. First launch the SMSWebServer (unblock the port if prompted) and launch the SMSClient. Then navigate your browser to the default.aspx page inside of the mobile_setting web site.

The bare bones web page looks like this:

subscribe.jpg

This page embeds Zeep's IFRAME. It checks against a database to be sure that the user and phone number that you are subscribing hasn't already been registered. I haven't included the code in this example to update the database yet, but the idea is that whenever a new subscriber subscribes to your service, the database should be updated with his/her user id and phone number. The reason I haven't finished this piece is that as of now, Zeep doesn't send the "STOP" event (which happens whenever a subscriber un-subscribes from your service), so there isn't a way to remove the subscriber from your database. Zeep says that they are working on it.

Enter the user id you wish to use. I suggest testuser since that is the user id I'm defaulting into the Windows client. For right now, just enter any number following the format into the phone number text box. 999-999-9999 will work just fine. The reason for this is that we will be using Zeep's test phone for the time being.

Once you pass the user id / phone number edits, you be prompted to (re)enter your phone number.

For testing purposes, click on the drop down box and select the +test option (it's at the bottom of the list). Then enter the number of your test phone. It should look something like this:

zeepconfirm.jpg

Click the confirm number button. Once Zeep confirms that you've subscribed correctly, navigate to the test phone that you created in Zeep. There should be a message waiting for you to answer. Type YES into the phone and hit enter.

If everything worked correctly, you should see the welcome message. If not, then you should see a communication error 100 message, which means Zeep can't contact the SMSWebServer.

Hopefully everything worked and you will now be able to send messages. Enter your SMS Prefix into the test phone and then your message. Hit enter and you should see your message appear in the server text box and in the client.

Type a message into the Windows client and click the Send button. You should see your message appear in your test phone. If not, then right click on the phone and select reload.

If you shut down IIS while a client is running, you'll be prompted to shut down the client.

Misc

Zeep includes the functionality to "Blast" a message out to all of your subscribers at once, but I haven’t included that functionality in this example.

Props

I need to give BIG props to several people.

  • Jeff Barnes, who wrote the WCF: Duplex Operations and UI Threads Beer Party app. Thanks Jeff.
  • Artur Sharipov and the folks at KODART. Artur wrote the article about the XF.Server. Thanks to them.
  • Sushant Bhatia. Sushant put together the piece that posts messages to Zeep. His blog is here. Thanks Sushant.
  • Sacha Barber wrote the beginner's guide to threading in .NET which can be found here. Thanks Sacha.
  • Sam Allen and Lion Shi. Sam and Lion wrote this really cool process checker that I'm using to ensure that only one instance of the SMSClient and the SMSWebServer can be launched at a time. Sam's site is http://dotnetperls.com/. Thanks Sam and Allen.
  • Sijin wrote the MessageBoxExLib article. While I'm not using his solution anywhere close to what it can do, I liked it so much I just thought I'd include it here. Thanks Sijin.
  • Finally I'd like to thank the many users and posters to this web site and the great many posters around the net that are willing to share their thoughts and ideas.

Revisions

I really wanted to get rid of the timer that was keeping the client alive. So I searched around a bit more and found a comment on the site regarding the ability to keep dual binding connections open indefinitely. The comment suggests to make the receiveTimeout="infinite" on the binding AND to make the inactivityTimeout="infinite" on the reliableSession in both the app.config and the web.config of both the client and server.

As far as I can tell, this appears to work.

However, once I made those changes and then tried to update the service references, none of the configuration came over.

The problem is with the reliableSession inactivityTimeout="infinite" at the server.

Remove it from the binding and the service references will update. Put it back and they don't.

So I haven't changed the code. I figured that decision would be best left up to you. If you would like to get rid of the timer, then just comment out the timer code in the SMSWebServer.cs file and add the following to the MessageService Web.config.

XML
<bindings>
   <wsDualHttpBinding>
      <binding name="WSDualHttpBinding_MessageService"bypassProxyOnLocal="false"
			useDefaultWebProxy="true"receiveTimeout="infinite">
         <reliableSession ordered="true" inactivityTimeout="infinite" />
      </binding>
   </wsDualHttpBinding>
</bindings>

Then change the receiveTimeout and the inactivityTimeout so they both equal "infinite" in the SMSClient app.config file.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)