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