我有一个必须使用Silverlight 4中的套接字发送和接收数据的类。 它必须实现一个预先存在的接口,所以有些东西可能看起来有些奇怪,但是 这是:
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打破它,然后继续调试它工作正常。
我真的无法弄清楚造成这个问题的原因, 并且没有其他信息。
有什么建议吗?
答案 0 :(得分:1)
决定将我的评论作为答案。
恕我直言,AutoResetEvent Class更适合您的需求。
AutoResetEvent clientDataSent = new AutoResetEvent(true);
public void Write(byte[] data)
{
// Wait till the Write operation gets a green light to proceed. Consider using a timeout.
clientDataSent.WaitOne();
clientArgs.SetBuffer(data, 0, data.Length);
bool willRaiseEvent = client.SendAsync(clientArgs);
// Write operation will get a signal either from ProcessSend (sync) or clientArgs_Completed (async),
if (!willRaiseEvent) ProcessSend(clientArgs);
}
void clientArgs_Completed(object sender, SocketAsyncEventArgs e)
{
bool throwInvalidOperationException = false;
switch (e.LastOperation)
{
...
default:
throwInvalidOperationException = true;
}
//Signal a waiting Write operation that it can proceed.
clientDataSent.Set();
if (throwInvalidOperationException) throw new Exception("Invalid operation completed");
}
答案 1 :(得分:0)
使用AutoResetEvent作为Jaroslav Jandek建议似乎解决了我的问题。 虽然如果您有任何关于如何改进此代码的建议,请随时这样做。
public class TcpDataTransportClient : IDataTransportService
{
private const string TCP_ADDRESS_SETTING = "tcpaddress";
private const string TCP_PORT_SETTING = "tcpport";
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;
AutoResetEvent clientDataSent = new AutoResetEvent(false);
AutoResetEvent clientConnected = new AutoResetEvent(false);
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);
}
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);
bool willRaiseEvent = socket.ReceiveAsync(e);
if (!willRaiseEvent)
ProcessReceive(e);
clientDataSent.Set();
}
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)
{
clientArgs.SetBuffer(data, 0, data.Length);
bool willRaiseEvent = client.SendAsync(clientArgs);
if (!willRaiseEvent)
ProcessSend(clientArgs);
clientDataSent.WaitOne();
}
}
现在我可以断开连接并连接多次。 但首先我调用SendAsync(),根据我的理解,它将在后台发送数据(大部分时间),然后下一个调用是.WaitOne(),它会阻塞线程直到数据实际发送完毕。连接也是如此。