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

Blogs

Mike Taulty's Blog

Elsewhere

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
Mike Taulty's Blog wrote Silverlight 4 Rough Notes: Networking
on Wed, Nov 18 2009 11:05 AM

Note – these posts are put together after a short time with Silverlight 4 as a way of providing pointers