2011-01-25 77 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