I have a class which has to send and receive data using sockets in Silverlight 4. It has to implement a pre-existing interface, so some things might look somewhat weird, but here it is:
public class TcpDataTransportClient : IDataTransportService
{
private const string TCP_ADDRESS_SETTING = "tcpaddress";
private const string TCP_PORT_SETTING = "tcpport";
private static ManualResetEvent clientConnected = new ManualResetEvent(false);
private static ManualResetEvent clientDataReceived = new ManualResetEvent(false);
private static ManualResetEvent clientDataSent = new ManualResetEvent(false);
private Dictionary<string, object> settings = new Dictionary<string, object>();
private IDataEncapsulator dataEncapsulator;
private IDataCollector dataCollector;
private Socket client;
private SocketAsyncEventArgs clientArgs;
public event DataReceivedHandler OnDataReceived;
public event DataSentHandler OnDataSent;
public TcpDataTransportClient()
{
}
public Dictionary<string, object> Settings
{
get
{
return this.settings;
}
set
{
this.settings = value;
}
}
public IDataEncapsulator DataEncapsulator
{
get
{
return this.dataEncapsulator;
}
set
{
this.dataEncapsulator = value;
}
}
public void Start(IDataCollector dataCollector)
{
this.dataCollector = dataCollector;
clientArgs = new SocketAsyncEventArgs();
client = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
clientArgs.Completed += clientArgs_Completed;
clientArgs.UserToken = client;
clientArgs.RemoteEndPoint = GetIPEndPoint();
client.ConnectAsync(clientArgs);
clientConnected.WaitOne();
}
private IPEndPoint GetIPEndPoint()
{
IPAddress ipAddress;
int tcpPort;
if (!IPAddress.TryParse(settings[TCP_ADDRESS_SETTING].ToString(), out ipAddress))
throw new ArgumentException(String.Format("Invalid setting for IP Address: '{0}'", TCP_ADDRESS_SETTING));
if (!int.TryParse(settings[TCP_PORT_SETTING].ToString(), out tcpPort))
throw new ArgumentException(String.Format("Invalid setting for TCP Port: '{0}'", TCP_PORT_SETTING));
return new IPEndPoint(ipAddress, tcpPort);
}
void clientArgs_Completed(object sender, SocketAsyncEventArgs e)
{
switch (e.LastOperation)
{
case SocketAsyncOperation.Connect:
ProcessConnect(e);
break;
case SocketAsyncOperation.Receive:
ProcessReceive(e);
break;
case SocketAsyncOperation.Send:
ProcessSend(e);
break;
default:
throw new Exception("Invalid operation completed");
}
}
private void ProcessConnect(SocketAsyncEventArgs e)
{
if (e.SocketError != SocketError.Success)
{
throw new SocketException((int)e.SocketError);
}
else
{
clientConnected.Set();
}
}
private void ProcessReceive(SocketAsyncEventArgs e)
{
if (e.SocketError == SocketError.Success)
{
var socket = e.UserToken as Socket;
var response = dataCollector.Collect(e.Buffer);
if (response != null)
{
if (this.OnDataReceived != null)
this.OnDataReceived(response);
clientDataReceived.Set();
}
else
{
bool willRaiseEvent = socket.ReceiveAsync(clientArgs);
if (!willRaiseEvent)
ProcessReceive(e);
}
}
else
{
throw new SocketException((int)e.SocketError);
}
}
private void ProcessSend(SocketAsyncEventArgs e)
{
if (e.SocketError == SocketError.Success)
{
var socket = e.UserToken as Socket;
if (OnDataSent != null)
OnDataSent(clientArgs.Buffer);
clientDataSent.Set();
clientDataReceived.Reset();
bool willRaiseEvent = socket.ReceiveAsync(e);
if (!willRaiseEvent)
ProcessReceive(e);
clientDataReceived.WaitOne();
}
else
{
throw new SocketException((int)e.SocketError);
}
}
public void Stop()
{
client.Shutdown(SocketShutdown.Send);
client.Close();
client.Dispose();
clientArgs.Dispose();
}
public void Write(byte[] data)
{
clientDataSent.Reset();
clientArgs.SetBuffer(data, 0, data.Length);
bool willRaiseEvent = client.SendAsync(clientArgs);
if (!willRaiseEvent)
ProcessSend(clientArgs);
clientDataSent.WaitOne();
}
}
The idea here is that every request (send data) is always answered by a response (receive data), and it works fine as long as you do not disconnect and create a new connection.
For example:
client.Connect();
client.ClearConfiguration(1);
var status = client.RequestStatusDetails(1);
client.Disconnect();
This code sends multiple requests and receives an answer to each of them. However, if you run the same code again (or in a loop), the connection is established but as soon as the code reaches this point:
public void Write(byte[] data)
{
clientDataSent.Reset();
clientArgs.SetBuffer(data, 0, data.Length);
bool willRaiseEvent = client.SendAsync(clientArgs);
if (!willRaiseEvent)
ProcessSend(clientArgs);
clientDataSent.WaitOne();
}
An exception will be thrown for client.SendAsync(clientArgs);
This is the exception:
An asynchronous socket operation is already in progress using this SocketAsyncEventArgs instance
If however you put a breakpoint just before this statement, let VS2010 break on it, then continue debugging it works fine.
I really can't figure out what's causing this problem, and there is no additional information.
Any suggestions?