Mike Taulty's Blog
Bits and Bytes from Microsoft UK
Silverlight 2 & Sockets
Mike Taulty's Blog

Mike's Badges

Follow on Twitter
View mike's profile on slideshare
Add to Technorati Favorites
CW Blog Awards

I was bored in my hotel room so I thought I'd have a bit of a play with Silverlight 2 and sockets - you can ( in beta 1 ) open sockets back to the site of origin within a restricted port range ( 4502 to 4532 ).

I wrote a little Silverlight UI ( it's a bit much to call it a UI as it only has one TextBlock on it :-) );

<Grid x:Name="LayoutRoot" Background="Gray">
    <TextBlock
      x:Name="theText"
      FontSize="24" 
      Text="Not set"
      HorizontalAlignment="Center"
      VerticalAlignment="Center"/>
    </Grid>

and then I wrote some code behind this to update the textblock based on a time that I receive from the server. I wrote a little class to try and help me with that ( no doubt riddled with bugs :-) );

  public class DateTimeEventArgs : EventArgs
  {
    public DateTime NewDateTime { get; set; }
  }
  public class SocketDateTimeReader
  {
    public string Host { get; set; }
    public int Port { get; set; }

    public SocketDateTimeReader ()
    {
      socket = new Socket(AddressFamily.InterNetwork,
        SocketType.Stream, ProtocolType.Tcp);      
    }
    public void Start()
    {
      Debug.Assert(!socket.Connected);
      Debug.Assert(!string.IsNullOrEmpty(this.Host));

      SocketAsyncEventArgs args = new SocketAsyncEventArgs()
      {
        RemoteEndPoint = new DnsEndPoint(
          this.Host, this.Port)
      };
      args.Completed += OnSocketConnected;

      if (!socket.ConnectAsync(args))
      {
        OnSocketConnected(null, args);
      }
    }
    public void Stop()
    {
    }
    void OnSocketConnected(object sender, SocketAsyncEventArgs args)
    {
      // Now connected to host. Wait for a time to come in...
      args.Completed -= OnSocketConnected;
      args.Completed += OnReadCompleted;

      PerformNextAsyncRead(args);
    }
    void PerformNextAsyncRead(SocketAsyncEventArgs args)
    {
      args.SetBuffer(buffer, byteCount,
        (buffer.Length - byteCount));

      if (!socket.ReceiveAsync(args))
      {
        // Read done sync.
        OnReadCompleted(null, args);
      }
    }
    void OnReadCompleted(object sender, SocketAsyncEventArgs e)
    {
      if (e.BytesTransferred > 0)
      {
        byteCount += e.BytesTransferred;

        if (byteCount == 8)
        {
          // We have a new time...
          Int64 intVal = BitConverter.ToInt64(buffer, 0);
          DateTime dt = DateTime.FromFileTimeUtc(intVal);          
          FireNewDateTime(dt);
          byteCount = 0;
        }
        PerformNextAsyncRead(e);
      }
      else
      {
        FireHostDisconnected();
      }
    }
    void FireHostDisconnected()
    {
      if (HostDisconnected != null)
      {
        HostDisconnected(this, null);
      }
    }
    void FireNewDateTime(DateTime t)
    {
      if (NewDateTime != null)
      {
        NewDateTime(this, new DateTimeEventArgs()
        {
          NewDateTime = t
        });
      }
    }
    public event EventHandler HostDisconnected;
    public event EventHandler<DateTimeEventArgs> NewDateTime;
    Socket socket;
    byte[] buffer = new byte[8];
    int byteCount; 
  }

and then I can just use that from my code-behind file to sync up its events;

  public partial class Page : UserControl
  {
    public Page()
    {
      InitializeComponent();

      this.Loaded += new RoutedEventHandler(Page_Loaded);
    }
    void Page_Loaded(object sender, RoutedEventArgs e)
    {
      reader = new SocketDateTimeReader()
      {
        Host = Application.Current.Host.Source.DnsSafeHost,
        Port = 4502
      };
      reader.NewDateTime += reader_NewDateTime;
      reader.HostDisconnected += reader_HostDisconnected;
      reader.Start();
    }
    void reader_HostDisconnected(object sender, EventArgs e)
    {
      Dispatcher.BeginInvoke(() =>
        theText.Text = "Disconnected");
    }
    void reader_NewDateTime(object sender, DateTimeEventArgs e)
    {
      Dispatcher.BeginInvoke(() =>
       theText.Text = e.NewDateTime.ToLongTimeString());
    }
    SocketDateTimeReader reader;
 }

and that's pretty much it other than I needed a server-side process for this to talk to so I wrote a little console application to sit in an endless loop and pump DateTimes down the wire to the client.

class Program
{
  static void Main(string[] args)
  {
    socket = new Socket(AddressFamily.InterNetwork,
      SocketType.Stream, ProtocolType.Tcp);

    socket.Bind(new IPEndPoint(IPAddress.Any,
      4502));

    socket.Listen(5);

    socket.BeginAccept(OnAccept, null);

    Console.ReadLine();
  }
  static void OnAccept(IAsyncResult result)
  {
    try
    {
      Socket connected = socket.EndAccept(result);

      socket.BeginAccept(OnAccept, null);

      ThreadPool.QueueUserWorkItem(
        delegate
        {
          while (true)
          {
            Thread.Sleep(500);
            DateTime dt = DateTime.Now;
            Int64 longVal = dt.ToFileTimeUtc();
            byte[] buffer = BitConverter.GetBytes(longVal);

            try
            {
              connected.Send(buffer);
            }
            catch
            {
              connected.Close();
              break;
            }
          }
        });
    }
    catch
    {
    }
  }
  static Socket socket;
}

Interesting stuff - I'm not sure in reality how often you'd actually use this rather than just having the Silverlight client poll ( clearly, in my simple example the Silverlight client could generate the time without a server! :-) ).


Posted Tue, Mar 18 2008 8:17 AM by mtaulty
Filed under:

Comments

Christopher Steen wrote Link Listing - March 18, 2008
on Wed, Mar 19 2008 4:12 AM
AJAX  ASP.NET AJAX callbacks to Web Methods in ASPX pages [Via: Frank Wang ] ASP.NET  DotNetOpenId 0.1.2...
Dew Drop - March 20, 2008 | Alvin Ashcraft's Morning Dew wrote Dew Drop - March 20, 2008 | Alvin Ashcraft's Morning Dew
on Thu, Mar 20 2008 6:19 AM
Community Blogs wrote Silverlight Cream for March 20, 2008 -- #229
on Thu, Mar 20 2008 11:56 AM
Tim Heuer revisits SL2 and WS, Jesse Liberty discussing how to get SL assistance, Bill Reiss on the DispatcherTimer
SilverlightShow.net wrote SilverlightShow.net
on Thu, Mar 20 2008 11:35 PM
I was bored in my hotel room so I thought I'd have a bit of a play with Silverlight 2 and sockets - you can ( in beta 1 ) open sockets back to the site of origin within a restricted port range ( 4502 to 4532 ).
Wöchentliche Rundablage: Silverlight 2, .NET, LINQ, ASP.NET MVC, Design | Code-Inside Blog wrote W&ouml;chentliche Rundablage: Silverlight 2, .NET, LINQ, ASP.NET MVC, Design | Code-Inside Blog
on Mon, Mar 24 2008 12:13 PM
Daniel Moth wrote Networking in Silverlight 2 Beta 1
on Tue, Apr 22 2008 6:49 AM
Networking in Silverlight 2 Beta 1
Michael Sync » Silverlight 2 beta1 - Best of SilverlightCream wrote Michael Sync &raquo; Silverlight 2 beta1 - Best of SilverlightCream
on Sun, Jun 29 2008 10:01 AM
(C) Mike Taulty, 2009. All rights reserved. The information in this weblog is provided "AS IS" with no warranties, and confers no rights. This weblog does not represent the thoughts, intentions, plans or strategies of my employer. It is solely my opinion. Inappropriate comments will be deleted at the authors discretion. All code samples are provided "AS IS" without warranty of any kind, either express or implied, including but not limited to the implied warranties of merchantability and/or fitness for a particular purpose.
Powered by Community Server (Non-Commercial Edition), by Telligent Systems