我有一个C#.Net 3.5应用程序,它可以向可能订阅特定多播组的任何人发送多播“Hello”数据包,然后侦听所有响应。因此,每隔X秒,我可以发送一个“Hello”数据包并记下每个响应的人。
打算像这样使用:
MulticastHello hello_ = new MulticastHello();
// alert our UI of any responses to the 'Hello'
hello_.ReceivedHelloResponse += OnHelloResponse;
// this timer function is triggered every X seconds
private void OnTimer(object sender, EventArgs e)
{
// stop listening for responses to the last 'hello'
hello_.CancelHello();
// send a new 'hello' and start listening for responses
hello_.SendHello("224.0.100.1");
}
不幸的是,我遇到了取消异步读取的问题。我的private void OnReceive(IAsyncResult ar)
函数偶尔会抛出一个System.ArgumentException
,表示“IAsyncResult对象未从此类的相应异步方法返回。”
如何可靠地取消异步套接字操作。或者,有更好的方法吗?
我的实施如下。
谢谢, PaulH
public class HelloResponseEventArgs : EventArgs { /*...*/ }
public class MulticastHello : IDisposable
{
public event EventHandler<HelloResponseEventArgs> ReceivedHelloResponse;
private Socket socket_;
private byte[] received_ = new byte[HelloResponse.Size];
private EndPoint responder_ = new IPEndPoint(IPAddress.Any, 0);
protected virtual void OnReceivedHelloResponse(HelloResponseEventArgs e)
{
EventHandler<HelloResponseEventArgs> evt = ReceivedHelloResponse;
if (null != evt)
evt(this, e);
}
private void OnReceive(IAsyncResult ar)
{
IPEndPoint ipendpoint = new IPEndPoint(IPAddress.Any, 0);
EndPoint endpoint = ipendpoint as EndPoint;
try
{
socket_.EndReceiveFrom(ar, ref endpoint);
}
catch (System.ObjectDisposedException)
{
// the read was canceled. This is expected.
return;
}
// decode the response and set the event
IPEndPoint remote = endpoint as IPEndPoint;
HelloResponse response = new HelloResponse(Deserialize<HelloPacket>(received_));
OnReceivedHelloResponse(new HelloResponseEventArgs(remote.Address.ToString(), response));
// keep receiving responses until canceled
socket_.BeginReceiveFrom(received_,
0,
received_.Length,
SocketFlags.None,
ref endpoint,
new AsyncCallback(OnReceive),
null);
}
// stop listening for responses to the hello frame
public void CancelHello()
{
if (socket_ != null)
socket_.Close();
}
// send an initial 'Hello' to the a multicast address. Start listening for responses
public void SendHello(string address)
{
socket_ = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
socket_.MulticastLoopback = false;
socket_.Ttl = 255;
HelloResponse send = new HelloResponse();
byte[] data = Serialize(send.Packet);
EndPoint remote = new IPEndPoint(IPAddress.Parse(address), 7);
socket_.SendTo(data, remote);
socket_.BeginReceiveFrom(received_,
0,
received_.Length,
SocketFlags.None,
ref responder_,
new AsyncCallback(OnReceive),
null);
}
#region IDisposable Members
/* close the socket on dispose*/
#endregion
}
答案 0 :(得分:3)
在BeginReceive中将套接字作为状态arg发送。即。
socket_.BeginReceiveFrom(
received_,
0,
received_.Length,
SocketFlags.None,
ref endpoint,
new AsyncCallback(OnReceive),
socket_);
然后在OnReceive中使用而不是类变量socket。即。
Socket socket = (Socket)ar.AsyncState;
socket.EndReceiveFrom(ar, ref endpoint);
这可确保EndReceiveFrom调用发生在调用BeginReceive的同一套接字上。然后正如PaulH提到的那样,只是在OnReceive中捕获ObjectDisposedException,这将是在套接字上调用Close()的信号。
-Oli
答案 1 :(得分:2)
Close()
不等待。它会立即退出。因此,如果下一个SendHello()在EndReceiveFrom()结束之前结束,它将抛出System.ArgumentException
。
解决方案是在Close()
中捕获System.ObjectDisposedException
后设置的OnReceive
调用后等待事件对象。
-PaulH