Published Tuesday, March 18, 2008 8:17 AM by mtaulty

Silverlight 2 & Sockets

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! :-) ).


Filed Under:

# Link Listing - March 18, 2008 @ Wednesday, March 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...

Christopher Steen

# Dew Drop - March 20, 2008 | Alvin Ashcraft's Morning Dew @ Thursday, March 20, 2008 6:19 AM

PingBack from http://www.alvinashcraft.com/2008/03/20/dew-drop-march-20-2008/

Dew Drop - March 20, 2008 | Alvin Ashcraft's Morning Dew

# Silverlight Cream for March 20, 2008 -- #229 @ Thursday, March 20, 2008 11:56 AM

Tim Heuer revisits SL2 and WS, Jesse Liberty discussing how to get SL assistance, Bill Reiss on the DispatcherTimer

Community Blogs

# SilverlightShow.net @ Thursday, March 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 ).

SilverlightShow.net

# W&ouml;chentliche Rundablage: Silverlight 2, .NET, LINQ, ASP.NET MVC, Design | Code-Inside Blog @ Monday, March 24, 2008 12:13 PM

PingBack from http://code-inside.de/blog/2008/03/24/wchentliche-rundablage-silverlight-2-net-linq-aspnet-mvc-design/

Wöchentliche Rundablage: Silverlight 2, .NET, LINQ, ASP.NET MVC, Design | Code-Inside Blog

# Networking in Silverlight 2 Beta 1 @ Tuesday, April 22, 2008 6:49 AM

Networking in Silverlight 2 Beta 1

Daniel Moth