2011-01-25 73 views
1

我有一个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 
} 

回答

2

Close()不等待。它立即退出。所以,如果下一个SendHello()在EndReceiveFrom()完成之前就会抛出System.ArgumentException

解决方法是在在OnReceive中被捕获时设置的Close()呼叫之后等待事件对象。

-PaulH

3

发送的插座,在BeginReceive状态ARG。即

socket_.BeginReceiveFrom(
     received_, 
     0, 
     received_.Length, 
     SocketFlags.None, 
     ref endpoint, 
     new AsyncCallback(OnReceive), 
     socket_); 

然后在OnReceive中使用它来代替类变量套接字。即

Socket socket = (Socket)ar.AsyncState; 
socket.EndReceiveFrom(ar, ref endpoint); 

这可确保EndReceiveFrom调用发生在调用BeginReceive的同一个套接字上。然后,PaulH提到只是在OnReceive中捕获ObjectDisposedException,这将成为套接字上调用Close()的信号。

-Oli