Setting Up the Sockets Client
Now that your server is all set, you need to create a client. To do this, you can copy the XAML from the duplex HTTP sample into a new Sockets.xaml user control. Then, in the code-behind, you need to set up some stuff. This is where, if you ask me, sockets are easier than duplex HTTP — it feels like far fewer methods and far less trouble to accomplish the same scenario. So first, adding the following namespace includes:
using System.Threading; using System.Net.Sockets; using System.Text;
Then add these class members:
SynchronizationContext _UiThread; Socket _Channel; DnsEndPoint _RemoteEndPoint;
bool Connected {get{ return _Channel != null && _Channel.Connected; }}
This is fairly similar to the duplex HTTP sample, and, in fact, you can copy the AppendServerMessage from that sample as well as the button click event-handler signatures, the first of which is the SubscriptionButton_Click handler:
void SubscriptionButton_Click(object sender, RoutedEventArgs e) {
AppendServerMessage("Already subscribed."); return;
AppendServerMessage("Subscribing to server notifications..."); _UiThread = SynchronizationContext.Current;
_Channel = new Socket(AddressFamily.InterNetwork,
SocketType.Stream, ProtocolType.Tcp); _RemoteEndPoint =
new DnsEndPoint(Application.Current.Host.Source.DnsSafeHost, 4502);
SocketAsyncEventArgs args = new SocketAsyncEventArgs(); args.RemoteEndPoint = _RemoteEndPoint; args.Completed += SocketConnectCompleted; _Channel.ConnectAsync(args);
The first part of this should look familiar from the duplex HTTP, just ensuring only one subscription for this client at a time and grabbing a reference to the UI thread context. Assuming that it is not already connected, this will create a new socket and set up a DnsEndPoint to the site of origin (that's the Application .Current.Host.Source bit) on port 4502. The important thing here, of course, is to connect on the port that the server is listening on, so if this were a real-world app, you'd have to publish that information to your clients somehow. Here, we're hard-coding for simplicity.
Now Silverlight uses the SocketAsyncEventArgs class as a sort of general socket communication facility, so you go ahead and create an instance, set the remote endpoint to the one just created, attach to the Completed event with the SocketConnectCompleted handler, and call ConnectAsync on the socket. When the connection completes, it will call SocketConnectCompleted:
void SocketConnectCompleted(object sender, SocketAsyncEventArgs args) {
_UiThread.Post(AppendServerMessage,
"Could not connect to server."); _Channel.Dispose(); _Channel = null; return;
args.Completed -= SocketConnectCompleted; args.Completed += ReceiveData; args.SetBuffer(new byte[2048], 0, 2048); _Channel.ReceiveAsync(args);
_UiThread.Post(AppendServerMessage, "Waiting for notifications...");
If the result of the connection attempt is not successful, you dispose of the socket and send a message to the user letting them know that. If it does succeed, you move on to the next step — receiving data. Again, the SocketAsyncEventArgs class is used; in fact, you can reuse it to conserve resources as done here. First, remove the SocketConnectCompleted event handler and attach instead the ReceiveData handler. Set up a buffer to specify how much to receive at a time; in this case, 2048 is rather arbitrary and actually way more than you need since you know that the server is just sending the server time. What you'd want to do is set it up to something reasonable that would handle most messages but that is not so large that it ties up too many resources. Then it goes ahead and puts itself into a state to receive data from the server.
Remember that once this client connected, if the server will enable its timer (if no other clients were already connected) and start sending server time updates every second, the ReceiveData method should be called almost immediately:
void ReceiveData(object sender, SocketAsyncEventArgs e) {
string notification = Encoding.UTF8.GetString(
e.Buffer, e.Offset, e.BytesTransferred); _UiThread.Post(AppendServerMessage, notification); _Channel.ReceiveAsync(e);
This method just grabs the bytes sent as a UTF8 string, posts those to the UI thread, and tells the socket to receive again (using the same SocketAsyncEventArgs instance). This loop will continue as long as the server keeps sending data and, of course, as long as the socket remains open, which leads us to the UnsubscriptionButton_Click handler:
void UnsubscriptionButton_Click(object sender, RoutedEventArgs e) {
AppendServerMessage("Unsubscribed.");
else
AppendServerMessage("Not subscribed.");
If connected, this will dispose of the socket, clear out the reference to it, and notify the user accordingly.
So that about wraps it up for the communications services. As noted, you're going to see a lot of samples in this book using the standard HTTP- and WCF-style communications, so those were omitted here, but you did cover the duplex HTTP and sockets in enough depth to give you a good understanding of what is involved in using them. If you have a need for duplex, real-time communication, those are your two best options in Silverlight. If the application is meant to run over the Web, your best bet is duplex HTTP; however, if you can require your clients to open up the right ports, sockets may be a more dependable solution, perhaps even a simpler solution.
Post a comment