我有一個類必須使用Silverlight 4中的套接字發送和接收數據。 它必須實現預先存在的接口,所以有些東西可能看起來有點奇怪,但 那就是:SL4 AsyncEventArgs在第二個Socket連接後拋出InvalidOperationException異常
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();
}
}
這裏的想法是,每一個請求(發送數據)總是由一個響應(接收數據)回答說,只要你沒有斷開,並創建一個新的連接,它工作正常。
例如:
client.Connect();
client.ClearConfiguration(1);
var status = client.RequestStatusDetails(1);
client.Disconnect();
此代碼發送多個請求,並接收回答他們每個人。 但是,如果再次運行相同的代碼(或循環),建立連接後 但只要代碼了這一點:
public void Write(byte[] data)
{
clientDataSent.Reset();
clientArgs.SetBuffer(data, 0, data.Length);
bool willRaiseEvent = client.SendAsync(clientArgs);
if (!willRaiseEvent)
ProcessSend(clientArgs);
clientDataSent.WaitOne();
}
的異常將被拋出client.SendAsync(clientArgs );
這是例外:
異步套接字操作 已經在使用此 的SocketAsyncEventArgs實例
然而,如果你把一個斷點僅此聲明之前, 讓VS2010突破進展在它上面,然後繼續調試它工作正常。
我真的不知道是什麼原因導致此問題, 並沒有任何其他信息。
有什麼建議嗎?
你有一些野生鎖定那裏。您的代碼不允許同時發送多個請求。禁止它或爲每個異步請求創建一個新的'SocketAsyncEventArgs'實例。 – 2010-08-03 08:11:13
哦......它真的不得不禁止多個請求,你能指出我做錯了什麼嗎? – TimothyP 2010-08-03 08:49:42
@TimothyP:在調用client.SendAsync之前,需要使用'clientDataSent.WaitOne',當操作完成時,使用'clientDataSent.Set()'。另外,使用'AutoResetEvent'而不是'ManualResetEvent'並考慮在操作完全沒有完成的情況下使用等待超時。 – 2010-08-03 09:08:40