Silverlight 2 and System.Net.Sockets.Socket
The new beta of Silverlight 2 introduces Sockets. The security model enforced by the System.Net.Sockets namespace in Silverlight 2 allows for a connection only back to the site or host of origin. So Silverlight 2 applications will be allowed to connect only to the host from which they were downloaded.
As the Web browser doesn't have a property of the IP address (as you may expect using sockets) there is a new endpoint class, the DnsEndPoint. To create a new instance of DnsEndPoint you have to specify the host name (as string e.g. from Application.Current.Host.Source.DnsSafeHost) and the port address.
string host = "mydomain.com"; int port = 80; DnsEndPoint endPoint = new DnsEndPoint(host, port);
All methods of the Socket class to connect, send or receive have to be used asynchronous. I've created a small helper class that wraps all the asynchronous operations. You can download the C# source here.
using System; using System.Net; using System.Net.Sockets; using System.Text; using System.Threading; namespace Networking { internal sealed class SocketClient : IDisposable { private const int Receive = 1; private const int Send = 0; private bool isConnected = false; private Socket socket; private DnsEndPoint endPoint; private static AutoResetEvent autoEvent = new AutoResetEvent(false); private static AutoResetEvent[] autoSendReceiveEvents = new AutoResetEvent[] { new AutoResetEvent(false), new AutoResetEvent(false) }; internal SocketClient(string host, int port) { endPoint = new DnsEndPoint(host, port); socket = new Socket(AddressFamily.InterNetwork /* hostEndPoint.AddressFamily */, SocketType.Stream, ProtocolType.Tcp); } internal void Connect() { SocketAsyncEventArgs args = new SocketAsyncEventArgs(); args.UserToken = socket; args.RemoteEndPoint = endPoint; args.Completed += new EventHandler<SocketAsyncEventArgs>(OnConnect); socket.ConnectAsync(args); autoEvent.WaitOne(); if (args.SocketError != SocketError.Success) throw new SocketException((int)args.SocketError); } internal void Disconnect() { socket.Close(); } #region Events private void OnConnect(object sender, SocketAsyncEventArgs e) { autoEvent.Set(); isConnected = (e.SocketError == SocketError.Success); } private void OnReceive(object sender, SocketAsyncEventArgs e) { autoSendReceiveEvents[Send].Set(); } private void OnSend(object sender, SocketAsyncEventArgs e) { autoSendReceiveEvents[Receive].Set(); if (e.SocketError == SocketError.Success) { if (e.LastOperation == SocketAsyncOperation.Send) { // Prepare receiving. Socket s = e.UserToken as Socket; byte[] response = new byte[255]; e.SetBuffer(response, 0, response.Length); e.Completed += new EventHandler<SocketAsyncEventArgs>(OnReceive); s.ReceiveAsync(e); } } else { ProcessError(e); } } #endregion private void ProcessError(SocketAsyncEventArgs e) { Socket s = e.UserToken as Socket; if (s.Connected) { try { s.Shutdown(SocketShutdown.Both); } catch (Exception) { } finally { if (s.Connected) s.Close(); } } throw new SocketException((int)e.SocketError); } internal String SendReceive(string message) { if (isConnected) { Byte[] bytes = Encoding.UTF8.GetBytes(message); SocketAsyncEventArgs args = new SocketAsyncEventArgs(); args.SetBuffer(bytes, 0, bytes.Length); args.UserToken = socket; args.RemoteEndPoint = endPoint; args.Completed += new EventHandler<SocketAsyncEventArgs>(OnSend); socket.SendAsync(args); AutoResetEvent.WaitAll(autoSendReceiveEvents); return Encoding.UTF8.GetString(args.Buffer, args.Offset, args.BytesTransferred); } else throw new SocketException((int)SocketError.NotConnected); } #region IDisposable Members public void Dispose() { autoEvent.Close(); autoSendReceiveEvents[Send].Close(); autoSendReceiveEvents[Receive].Close(); if (socket.Connected) socket.Close(); } #endregion } }
14 Comments
Comments have been disabled for this content.
jorgie said
I have seen multiple blog entries that say Silverlight 2.0 supports the Flash Cross Domain Policy file format. Doesn't that allow you to specificy other trusted domains from the server side?
Michael Schwarz said
@jorgie: The documentation says that Sockets are only working to the download URI of the assembly. It is different to WebRequests. Michael
Randolpho said
I'm glad Silverlight 2 will allow socket-based connections, and I'm equally glad that it's restricting the connections for security reasons. There are some instances I can see, however, where it might be useful (architecturally speaking) to connect to a separate host. I want the cross-site scripting security however... So what about subdomains? Would a Silverlight application that was obtained from, say, myhost.com be able to connect to, for example, chat.myhost.com? A quick search turned up nothing... any ideas?
Gadfly said
Great, cant wait to get sockets up and running ... alas i cant. I have tried out your code having a local socket host running but i get "An attempt was made to access a socket in a way forbidden by its access permissions" exception event though im using DnsSafeHost as host name. Iv tried both with web solution and HTML generated SL projects. Are there any special considerations when im connecting to localhost ?
Corey said
I also received the following error: System.Net.Sockets.SocketException: An attempt was made to access a socket in a way forbidden by its access permissions. What gives?
Michael Schwarz said
@Gadfy, @Corey: which port are you trying to connect to? Michael
Corey said
@Michael: I'm trying to connect to port 6000 and using my local machine's IP address as my host. Lastly, I have a light weight server running locally listening in on port 6000. The System.Net.Sockets.SocketException occurs during the connect method.
Michael Schwarz said
@Corey: have a look at my post here: The allowed port range is 4502..4532, and the port where the Silverlight application files are downloaded from (default for Web applications on http is 80). Hope that helps! Michael
Corey said
@Michael: That worked! Thanks! Something else I discovered that others may find useful: It only worked when I used a host name (i.e. localhost). This may be due to the reverse DNS lookup requirement found in the early version of Silverlight 2. Using an IP address throws the exception so make sure you have localhost defined in C:\Windows\System32\Drivers\etc\host.
aladdin said
this security policy greatly reduced the possibility of the use of socket. I have written a stock trading program for the last two years in Flex. I have learned that it is impossible to connect socket port other than 80 and 443 "in the real world", and you know iis occupied them brutely. How could I port my stock trading program to silverlight? I need a dedicated web server to download silverlight, trading webservice, and a dedicated Socket server to push stock price, price alert, trading message through 80 and 443. We need the same mechanism that Flash player has provided. Consider the crossdomain of socket twice. You need it, I guarentee.
Amit said
I read somewhere else that this is just a stopgap restriction for the beta, but that the full release of Silverlight 2 will have cross domain functionality for sockets. Can somebody confirm this?
Robert Folkesson (MSFT) said
Yes the site of origin restriction will go away in future release.
kiseok7 said
Thanks for post, There is a way to use this in Page Usercontrol by inheritance?
Dave Murray (MSFT) said
Nice writeup, Michael. However, the synchronous methods were removed from Sockets quite intentionally. Unless you specifically move blocking calls to a background thread (using BackgroundWorker or ThreadPool.QueueUserWorkItem, for example), you will prevent the browser UI from being updated for the duration of the call. Connect can take up to a minute, which is a long time for the browser to be completely hung. Even worse, Receive is completely dependent on the server you're connected to. There is no guarantee that it will EVER complete. It is STRONGLY recommended that developers take the time to learn and use the asynchronous, event-driven model we have provided. Forcing these methods to be synchronous may seem easier, but it is almost certainly a Bad Idea(tm) for your application.