Tag Archives: Long Polling

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.

Tagged , , , , ,