Real-time Client-Server Communication on Windows Phone with Long Polling

I was looking for some information on Push technologies and the first thing I found was the Wikipedia article which among other things mentioned a technique called “Long Polling”. This seemed like a really feasible solution to the problem I have seen many people struggling with – getting real-time communication from the phone. Now I can see some limitations – an app needing to keep running to keep it from losing connection (this might change with one of the upcoming updates though if it enables multitasking), possible data usage or server security exposure problems, but I have little experience with server development, so why would I worry about that? Smile

I decided to try writing a Windows Phone 7 app with a back-end WCF service and a non-WP7-Silverlight app to talk to. This is simple – just create a solution in VS and a project for each of these three programs.

image

Let’s start with the WCF service.

Configure the WCF service to run on a specific port – go to project/properties/web and set “Specific port” to 2333. This will give you a reliable location to reference the service.

Add clientaccesspolicy.xml to the WCF service to let the SL4 app connect.

Rename (Refactor/Rename) Service1 to ChatService and define the service contract:

[ServiceContract]
public interface IChatService
{
    [OperationContract]
    void Send(string message, string recipient);
 
    [OperationContract]
    string GetMessage(string recipient);
}

As you can see – the service class will be simplified to only send messages to a given recipient and let a recipient poll for a message. This is the service class definition:

public class ChatService : IChatService
{
    // A list of messages for each recipient
    private static Dictionary<string, List<string>> messages = 
        new Dictionary<string, List<string>>();
 
    private static object syncObject = 
        new object();
 
    private static readonly TimeSpan Timeout = 
        TimeSpan.FromSeconds(50);
    
    public void Send(string message, string recipient)
    {
        lock (syncObject)
        {
            if (!messages.ContainsKey(recipient))
                messages.Add(recipient, new List<string>());
 
            messages[recipient].Add(message);
        }
    }
 
    public string GetMessage(string recipient)
    {
        var start = DateTime.Now;
 
        do
        {
            lock (syncObject)
            {
                if (!messages.ContainsKey(recipient))
                    messages.Add(recipient, new List<string>());
                else
                {
                    if (messages[recipient].Count > 0)
                    {
                        var message = messages[recipient][0];
                        messages[recipient].RemoveAt(0);
                        return message;
                    }
                }
 
                // Yes, just faking it out a bit to avoid the distractions from events,
                // resetevents etc.
                Thread.Sleep(50);
            }
        } while (DateTime.Now - start < Timeout);
 
        return null;
    }
}

We have a dictionary that has a list of messages for each registered recipient, the Send method just adds a message to the list and the GetMessage keeps polling to see if there is a new message in the list, then either returns one if one shows up or times out and returns null. The client will handle nulls and timeouts and interpret them as the good time to start another long poll.

Now do both WP7 and SL4 apps in parallel

Add service reference to both Silverlight projects, referencing http://localhost:2333/ChatService.svc as ChatService.

Now edit MainPage.xaml files for both apps – set x:Name=”root” on the root elements, then add this simplistic view as its Content shared by both apps:

 <Grid
    x:Name="LayoutRoot"
    Loaded="LayoutRoot_Loaded">

    <Grid.RowDefinitions>
        <RowDefinition Height="*"/>
        <RowDefinition Height="Auto"/>
    </Grid.RowDefinitions>
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="*"/>
        <ColumnDefinition Width="Auto"/>
    </Grid.ColumnDefinitions>
 
    <ListBox
        Grid.Row="0"
        Grid.ColumnSpan="2"
        ItemsSource="{Binding Messages, ElementName=root}" />

    <TextBox 
        x:Name="InputBox"
        Grid.Row="1"
        Grid.Column="0"
        Text="{Binding Message, Mode=TwoWay, ElementName=root}" />

    <Button
        x:Name="SendButton"
        Grid.Row="1"
        Grid.Column="1"
        Click="SendButton_Click"
        Content="Send"
        Padding="10,0" />

</Grid>

The code behind for both will be almost identical – just swap recipients. The one below is for the WP7 version – it sends messages to “SLPC” and gets messages for “WP7”.

public partial class MainPage : PhoneApplicationPage
{
    public static readonly DependencyProperty MessagesProperty =
        DependencyProperty.Register("Messages", typeof(ObservableCollection<string>), typeof(MainPage),
            new PropertyMetadata(null));
 
    public ObservableCollection<string> Messages
    {
        get { return (ObservableCollection<string>)GetValue(MessagesProperty); }
        set { SetValue(MessagesProperty, value); }
    }
 
    public static readonly DependencyProperty MessageProperty =
        DependencyProperty.Register("Message", typeof(string), typeof(MainPage),
            new PropertyMetadata(""));
 
    public string Message
    {
        get { return (string)GetValue(MessageProperty); }
        set { SetValue(MessageProperty, value); }
    }
 
    // Constructor
    public MainPage()
    {
        this.DataContext = this;
        Messages = new ObservableCollection<string>();
        InitializeComponent();
    }
 
    private void SendButton_Click(object sender, RoutedEventArgs e)
    {
        var svc = new ChatService.ChatServiceClient();
        svc.SendAsync(Message, "SLPC");
        Message = "";
    }
 
    private void LayoutRoot_Loaded(object sender, RoutedEventArgs e)
    {
        var svc = new ChatService.ChatServiceClient();
        svc.GetMessageCompleted += OnGetMessageCompleted;
        svc.GetMessageAsync("WP7");
    }
 
    private void OnGetMessageCompleted(object sender, ChatService.GetMessageCompletedEventArgs e)
    {
        var svc = (ChatService.ChatServiceClient) sender;
 
        if (e.Error is TimeoutException ||
            (e.Error == null && e.Result == null))
        {
            svc.GetMessageAsync("WP7");
            return;
        }
 
        if (e.Error != null)
            MessageBox.Show(e.Error.Message);
        else
        {
            Messages.Add(e.Result);
        }
 
        svc.GetMessageAsync("WP7");
    }
}

As you can see we have two dependency properties for the view to bind to – the list of Messages and the Message to send. Clicking on Send will simply send the message through the service proxy, while as soon as the page is loaded – polling on GetMessage starts and OnGetMessageCompleted will check for timeouts and nulls, show any messages incoming and reissue requests to GetMessage.

Conclusion

What we get is this – a Windows Phone and a Silverlight 4 apps talking to one another with virtually no delay.

image

Now there are a lot of simplifications here – no authentication of clients, hardcoded recipients, the server using polling to check for new messages from other threads etc. but it works!

I am sure this can be improved and perhaps there are better or simpler ways to achieve this and I am open to comments.

Advertisements
Tagged , , , , ,

5 thoughts on “Real-time Client-Server Communication on Windows Phone with Long Polling

  1. bar123 says:

    Excellent job!!!
    Could you please share your sample code?

    Thanks

  2. Anonymous says:

    You set a timeout of 50 seconds. According that windows phone devices have a default and immutable timeout of 60 seconds, how could you get it work if service timeout is over 60 seconds ? (Using eumaltor is another problem, because it does not follow this behavior).
    This is a problem i’m encoutering.
    Thanks 😉

  3. xyzzer says:

    It’s surprising how this post still sees quite many views even if this is only for an experiment that I have never put into any production application! 🙂

    I don’t want to dig too deep into my old code, but I would have the client and server exchange a message every 50s saying there is nothing new on the server, so every 50s the client would start another long poll and that should never (big word) reach 50s.

  4. xyzzer says:

    This is still my most popular post to date. I am wondering – have you used this code in a real application? Does it make sense or is there a simpler way to do all this with say WCF? There seems to be a popular library called SignalR that has a .NET version – https://github.com/SignalR/SignalR – that must be better than my unproven code here…

    Interestingly though – this post predates SignalR (2011-03-10 vs. 2011-07-21). 🙂

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: