Silverlight 2 and System.Net.Sockets.Socket

Michael Schwarz on Friday, March 7, 2008

The new beta of Silverlight 2 [1] 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.

<pre style="OVERFLOW: auto; BACKGROUND-COLOR: white"><div><!--

Code highlighting produced by Actipro CodeHighlighter (freeware) http://www.CodeHighlighter.com/

DnsEndPoint endPoint </span><span style="COLOR: #000000">=</span><span style="COLOR: #000000"> </span><span style="COLOR: #0000ff">new</span><span style="COLOR: #000000"> DnsEndPoint(host, port);</span></div></pre><!-- Code inserted with Steve Dunn's Windows Live Writer Code Formatter Plugin. http://dunnhq.com -->

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 [2].

<pre style="OVERFLOW: auto; BACKGROUND-COLOR: white"><div><!--

Code highlighting produced by Actipro CodeHighlighter (freeware) http://www.CodeHighlighter.com/

</span><span style="COLOR: #0000ff">using</span><span style="COLOR: #000000"> System.Net.Sockets; </span><span style="COLOR: #0000ff">using</span><span style="COLOR: #000000"> System.Text; </span><span style="COLOR: #0000ff">using</span><span style="COLOR: #000000"> System.Threading;

</span><span style="COLOR: #0000ff">namespace</span><span style="COLOR: #000000"> Networking { </span><span style="COLOR: #0000ff">internal</span><span style="COLOR: #000000"> </span><span style="COLOR: #0000ff">sealed</span><span style="COLOR: #000000"> </span><span style="COLOR: #0000ff">class</span><span style="COLOR: #000000"> SocketClient : IDisposable { </span><span style="COLOR: #0000ff">private</span><span style="COLOR: #000000"> </span><span style="COLOR: #0000ff">const</span><span style="COLOR: #000000"> </span><span style="COLOR: #0000ff">int</span><span style="COLOR: #000000"> Receive </span><span style="COLOR: #000000">=</span><span style="COLOR: #000000"> </span><span style="COLOR: #800080">1</span><span style="COLOR: #000000">; </span><span style="COLOR: #0000ff">private</span><span style="COLOR: #000000"> </span><span style="COLOR: #0000ff">const</span><span style="COLOR: #000000"> </span><span style="COLOR: #0000ff">int</span><span style="COLOR: #000000"> Send </span><span style="COLOR: #000000">=</span><span style="COLOR: #000000"> </span><span style="COLOR: #800080">0</span><span style="COLOR: #000000">;

</span><span style="COLOR: #0000ff">private</span><span style="COLOR: #000000"> </span><span style="COLOR: #0000ff">bool</span><span style="COLOR: #000000"> isConnected </span><span style="COLOR: #000000">=</span><span style="COLOR: #000000"> </span><span style="COLOR: #0000ff">false</span><span style="COLOR: #000000">;

</span><span style="COLOR: #0000ff">private</span><span style="COLOR: #000000"> Socket socket; </span><span style="COLOR: #0000ff">private</span><span style="COLOR: #000000"> DnsEndPoint endPoint;

</span><span style="COLOR: #0000ff">private</span><span style="COLOR: #000000"> </span><span style="COLOR: #0000ff">static</span><span style="COLOR: #000000"> AutoResetEvent autoEvent </span><span style="COLOR: #000000">=</span><span style="COLOR: #000000"> </span><span style="COLOR: #0000ff">new</span><span style="COLOR: #000000"> AutoResetEvent(</span><span style="COLOR: #0000ff">false</span><span style="COLOR: #000000">); </span><span style="COLOR: #0000ff">private</span><span style="COLOR: #000000"> </span><span style="COLOR: #0000ff">static</span><span style="COLOR: #000000"> AutoResetEvent[] autoSendReceiveEvents </span><span style="COLOR: #000000">=</span><span style="COLOR: #000000"> </span><span style="COLOR: #0000ff">new</span><span style="COLOR: #000000"> AutoResetEvent[] { </span><span style="COLOR: #0000ff">new</span><span style="COLOR: #000000"> AutoResetEvent(</span><span style="COLOR: #0000ff">false</span><span style="COLOR: #000000">), </span><span style="COLOR: #0000ff">new</span><span style="COLOR: #000000"> AutoResetEvent(</span><span style="COLOR: #0000ff">false</span><span style="COLOR: #000000">) };

</span><span style="COLOR: #0000ff">internal</span><span style="COLOR: #000000"> SocketClient(</span><span style="COLOR: #0000ff">string</span><span style="COLOR: #000000"> host, </span><span style="COLOR: #0000ff">int</span><span style="COLOR: #000000"> port) { endPoint </span><span style="COLOR: #000000">=</span><span style="COLOR: #000000"> </span><span style="COLOR: #0000ff">new</span><span style="COLOR: #000000"> DnsEndPoint(host, port); socket </span><span style="COLOR: #000000">=</span><span style="COLOR: #000000"> </span><span style="COLOR: #0000ff">new</span><span style="COLOR: #000000"> Socket(AddressFamily.InterNetwork </span><span style="COLOR: #008000">/</span><span style="COLOR: #008000"> hostEndPoint.AddressFamily </span><span style="COLOR: #008000">/</span><span style="COLOR: #000000">, SocketType.Stream, ProtocolType.Tcp); }

</span><span style="COLOR: #0000ff">internal</span><span style="COLOR: #000000"> </span><span style="COLOR: #0000ff">void</span><span style="COLOR: #000000"> Connect() { SocketAsyncEventArgs args </span><span style="COLOR: #000000">=</span><span style="COLOR: #000000"> </span><span style="COLOR: #0000ff">new</span><span style="COLOR: #000000"> SocketAsyncEventArgs();

args.UserToken </span><span style="COLOR: #000000">=</span><span style="COLOR: #000000"> socket; args.RemoteEndPoint </span><span style="COLOR: #000000">=</span><span style="COLOR: #000000"> endPoint; args.Completed </span><span style="COLOR: #000000">+=</span><span style="COLOR: #000000"> </span><span style="COLOR: #0000ff">new</span><span style="COLOR: #000000"> EventHandler</span><span style="COLOR: #000000">&lt;</span><span style="COLOR: #000000">SocketAsyncEventArgs</span><span style="COLOR: #000000">&gt;</span><span style="COLOR: #000000">(OnConnect);

socket.ConnectAsync(args); autoEvent.WaitOne();

</span><span style="COLOR: #0000ff">if</span><span style="COLOR: #000000"> (args.SocketError </span><span style="COLOR: #000000">!=</span><span style="COLOR: #000000"> SocketError.Success) </span><span style="COLOR: #0000ff">throw</span><span style="COLOR: #000000"> </span><span style="COLOR: #0000ff">new</span><span style="COLOR: #000000"> SocketException((</span><span style="COLOR: #0000ff">int</span><span style="COLOR: #000000">)args.SocketError); }

</span><span style="COLOR: #0000ff">internal</span><span style="COLOR: #000000"> </span><span style="COLOR: #0000ff">void</span><span style="COLOR: #000000"> Disconnect() { socket.Close(); }

</span><span style="COLOR: #0000ff">#region</span><span style="COLOR: #000000"> Events</span><span style="COLOR: #000000">

</span><span style="COLOR: #0000ff">private</span><span style="COLOR: #000000"> </span><span style="COLOR: #0000ff">void</span><span style="COLOR: #000000"> OnConnect(</span><span style="COLOR: #0000ff">object</span><span style="COLOR: #000000"> sender, SocketAsyncEventArgs e) { autoEvent.Set(); isConnected </span><span style="COLOR: #000000">=</span><span style="COLOR: #000000"> (e.SocketError </span><span style="COLOR: #000000">==</span><span style="COLOR: #000000"> SocketError.Success); }

</span><span style="COLOR: #0000ff">private</span><span style="COLOR: #000000"> </span><span style="COLOR: #0000ff">void</span><span style="COLOR: #000000"> OnReceive(</span><span style="COLOR: #0000ff">object</span><span style="COLOR: #000000"> sender, SocketAsyncEventArgs e) { autoSendReceiveEvents[Send].Set(); }

</span><span style="COLOR: #0000ff">private</span><span style="COLOR: #000000"> </span><span style="COLOR: #0000ff">void</span><span style="COLOR: #000000"> OnSend(</span><span style="COLOR: #0000ff">object</span><span style="COLOR: #000000"> sender, SocketAsyncEventArgs e) { autoSendReceiveEvents[Receive].Set();

</span><span style="COLOR: #0000ff">if</span><span style="COLOR: #000000"> (e.SocketError </span><span style="COLOR: #000000"></span><span style="COLOR: #000000"> SocketError.Success) { </span><span style="COLOR: #0000ff">if</span><span style="COLOR: #000000"> (e.LastOperation </span><span style="COLOR: #000000"></span><span style="COLOR: #000000"> SocketAsyncOperation.Send) { </span><span style="COLOR: #008000">//</span><span style="COLOR: #008000"> Prepare receiving.</span><span style="COLOR: #008000"> </span><span style="COLOR: #000000"> Socket s </span><span style="COLOR: #000000">=</span><span style="COLOR: #000000"> e.UserToken </span><span style="COLOR: #0000ff">as</span><span style="COLOR: #000000"> Socket;

</span><span style="COLOR: #0000ff">byte</span><span style="COLOR: #000000">[] response </span><span style="COLOR: #000000">=</span><span style="COLOR: #000000"> </span><span style="COLOR: #0000ff">new</span><span style="COLOR: #000000"> </span><span style="COLOR: #0000ff">byte</span><span style="COLOR: #000000">[</span><span style="COLOR: #800080">255</span><span style="COLOR: #000000">]; e.SetBuffer(response, </span><span style="COLOR: #800080">0</span><span style="COLOR: #000000">, response.Length); e.Completed </span><span style="COLOR: #000000">+=</span><span style="COLOR: #000000"> </span><span style="COLOR: #0000ff">new</span><span style="COLOR: #000000"> EventHandler</span><span style="COLOR: #000000">&lt;</span><span style="COLOR: #000000">SocketAsyncEventArgs</span><span style="COLOR: #000000">&gt;</span><span style="COLOR: #000000">(OnReceive); s.ReceiveAsync(e); } } </span><span style="COLOR: #0000ff">else</span><span style="COLOR: #000000"> { ProcessError(e); } }

</span><span style="COLOR: #0000ff">#endregion</span><span style="COLOR: #000000">

</span><span style="COLOR: #0000ff">private</span><span style="COLOR: #000000"> </span><span style="COLOR: #0000ff">void</span><span style="COLOR: #000000"> ProcessError(SocketAsyncEventArgs e) { Socket s </span><span style="COLOR: #000000">=</span><span style="COLOR: #000000"> e.UserToken </span><span style="COLOR: #0000ff">as</span><span style="COLOR: #000000"> Socket; </span><span style="COLOR: #0000ff">if</span><span style="COLOR: #000000"> (s.Connected) { </span><span style="COLOR: #0000ff">try</span><span style="COLOR: #000000"> { s.Shutdown(SocketShutdown.Both); } </span><span style="COLOR: #0000ff">catch</span><span style="COLOR: #000000"> (Exception) { } </span><span style="COLOR: #0000ff">finally</span><span style="COLOR: #000000"> { </span><span style="COLOR: #0000ff">if</span><span style="COLOR: #000000"> (s.Connected) s.Close(); } }

</span><span style="COLOR: #0000ff">throw</span><span style="COLOR: #000000"> </span><span style="COLOR: #0000ff">new</span><span style="COLOR: #000000"> SocketException((</span><span style="COLOR: #0000ff">int</span><span style="COLOR: #000000">)e.SocketError); }

</span><span style="COLOR: #0000ff">internal</span><span style="COLOR: #000000"> String SendReceive(</span><span style="COLOR: #0000ff">string</span><span style="COLOR: #000000"> message) { </span><span style="COLOR: #0000ff">if</span><span style="COLOR: #000000"> (isConnected) { Byte[] bytes </span><span style="COLOR: #000000">=</span><span style="COLOR: #000000"> Encoding.UTF8.GetBytes(message);

SocketAsyncEventArgs args </span><span style="COLOR: #000000">=</span><span style="COLOR: #000000"> </span><span style="COLOR: #0000ff">new</span><span style="COLOR: #000000"> SocketAsyncEventArgs(); args.SetBuffer(bytes, </span><span style="COLOR: #800080">0</span><span style="COLOR: #000000">, bytes.Length); args.UserToken </span><span style="COLOR: #000000">=</span><span style="COLOR: #000000"> socket; args.RemoteEndPoint </span><span style="COLOR: #000000">=</span><span style="COLOR: #000000"> endPoint; args.Completed </span><span style="COLOR: #000000">+=</span><span style="COLOR: #000000"> </span><span style="COLOR: #0000ff">new</span><span style="COLOR: #000000"> EventHandler</span><span style="COLOR: #000000">&lt;</span><span style="COLOR: #000000">SocketAsyncEventArgs</span><span style="COLOR: #000000">&gt;</span><span style="COLOR: #000000">(OnSend);

socket.SendAsync(args);

AutoResetEvent.WaitAll(autoSendReceiveEvents);

</span><span style="COLOR: #0000ff">return</span><span style="COLOR: #000000"> Encoding.UTF8.GetString(args.Buffer, args.Offset, args.BytesTransferred); } </span><span style="COLOR: #0000ff">else</span><span style="COLOR: #000000"> </span><span style="COLOR: #0000ff">throw</span><span style="COLOR: #000000"> </span><span style="COLOR: #0000ff">new</span><span style="COLOR: #000000"> SocketException((</span><span style="COLOR: #0000ff">int</span><span style="COLOR: #000000">)SocketError.NotConnected); }

</span><span style="COLOR: #0000ff">#region</span><span style="COLOR: #000000"> IDisposable Members</span><span style="COLOR: #000000">

</span><span style="COLOR: #0000ff">public</span><span style="COLOR: #000000"> </span><span style="COLOR: #0000ff">void</span><span style="COLOR: #000000"> Dispose() { autoEvent.Close(); autoSendReceiveEvents[Send].Close(); autoSendReceiveEvents[Receive].Close(); </span><span style="COLOR: #0000ff">if</span><span style="COLOR: #000000"> (socket.Connected) socket.Close(); }

</span><span style="COLOR: #0000ff">#endregion</span><span style="COLOR: #000000"> } }</span></div></pre><!-- Code inserted with Steve Dunn's Windows Live Writer Code Formatter Plugin. http://dunnhq.com -->