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
}
}