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
mobile_settings web site/page is used to subscribe new users and is where the whole thing begins.
SMSClient sends and receives text messages to and from users who have subscribed to the service.
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.
MessageService sends subscriber's text messages from the
SMSWebServer to the
SMSService is used to send text messages from the Windows
SMSClient to a subscriber via Zeep.
- The ZeepmobileSMS.dll posts messages to Zeep and is used by the
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
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.
- Go into the web.config file for the
MessageService and change the endpoint address (and dns property) to point to your domain.
- 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.
- 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.
The Windows Client
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...
... property on the
wsDualHttpBinding AND the...
... 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
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
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 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.
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: (
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.
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:
_messageService = new MessageServiceClient(new InstanceContext(this),
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:
internal static AsyncObject _ao = new AsyncObject();
internal static InstanceContext _instanceContext = new InstanceContext(_ao);
... and then used it like this inside the
static OnReadComplete event:
MessageServiceClient messageService = null;
messageService = new MessageServiceClient
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:
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:
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
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.
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.
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.
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
<reliableSession ordered="true" inactivityTimeout="infinite" />
Then change the
receiveTimeout and the
inactivityTimeout so they both equal "infinite" in the SMSClient app.config file.