2010-03-27 111 views
3

我對在C#中使用異步套接字方法的正確方式感到困惑。我將參考這兩篇文章來解釋一些事情並提出我的問題:MSDN article on asynchronous client socketsdevarticles.com article on socket programmingC中的異步套接字#

我的問題是關於BeginReceive()方法。 MSDN文章使用這兩個函數來處理接收數據:雖然devarticles.com教程爲BeginReceive方法的最後一個參數傳遞null

private static void Receive(Socket client) 
{ 
    try 
    { 
    // Create the state object. 
    StateObject state = new StateObject(); 
    state.workSocket = client; 

    // Begin receiving the data from the remote device. 
    client.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0, 
     new AsyncCallback(ReceiveCallback), state); 
    } 
    catch (Exception e) 
    { 
    Console.WriteLine(e.ToString()); 
    } 
} 

private static void ReceiveCallback(IAsyncResult ar) 
{ 
    try 
    { 
    // Retrieve the state object and the client socket 
    // from the asynchronous state object. 
    StateObject state = (StateObject) ar.AsyncState; 
    Socket client = state.workSocket; 
    // Read data from the remote device. 
    int bytesRead = client.EndReceive(ar); 
    if (bytesRead > 0) 
    { 
     // There might be more data, so store the data received so far. 
     state.sb.Append(Encoding.ASCII.GetString(state.buffer,0,bytesRead)); 
     // Get the rest of the data. 
     client.BeginReceive(state.buffer,0,StateObject.BufferSize,0, 
      new AsyncCallback(ReceiveCallback), state); 
    } 
    else 
    { 
     // All the data has arrived; put it in response. 
     if (state.sb.Length > 1) 
     { 
     response = state.sb.ToString(); 
     } 
     // Signal that all bytes have been received. 
     receiveDone.Set(); 
    } 
    } 
    catch (Exception e) 
    { 
    Console.WriteLine(e.ToString()); 
    } 
} 

,並接着解釋說,最後一個參數是有用的,當我們」重新處理多個套接字。現在,我的問題是:

  1. 什麼是傳遞一個狀態到BeginReceive方法,如果我們只用一個單一的插座工作的呢?是否要避免使用類字段?這似乎沒有什麼意義,但也許我錯過了一些東西。

  2. 如何與多個插座打交道時的狀態參數的幫助?如果我打電話給client.BeginReceive(...),是不是所有的數據都會從client插座上讀取?該devarticles.com教程使它在此調用聽起來像: m_asynResult = m_socClient.BeginReceive (theSocPkt.dataBuffer,0,theSocPkt.dataBuffer.Length, SocketFlags.None,pfnCallBack,theSocPkt);

數據將從theSocPkt.thisSocket套接字讀取,而不是從m_socClient插座。在他們的例子中,兩者是一樣的,但如果情況並非如此,會發生什麼?

我實在不明白這其中最後一個參數是非常有用的,或至少它如何與多個插座幫助。如果我有多個套接字,我仍然需要每個都調用BeginReceive,對吧?

回答

2

如果我們只使用一個套接字,那麼將狀態傳遞給BeginReceive方法有什麼意義?是否要避免使用類字段?這似乎沒有什麼意義,但也許我錯過了一些東西。

你說得對,如果你沒有使用狀態,那麼你必須改用成員。但是這比局部變量本地更少。當地的東西越多,當你在代碼的其他部分進行更改時,它們越不可能中斷。

它比較普通的方法調用。爲什麼我們不把參數設置爲成員,然後調用沒有任何參數的所有函數?它會工作......但是閱讀代碼會很糟糕。通過保持範圍儘可能局部化,使設計更容易理解和修改。改進的封裝導致更健壯的代碼。

同樣適用於這裏。如果你只有一個異步回調,那麼你可能只需要在課堂上設置一個成員就可以逃脫,但如果你有很多這樣的調用,那麼這個策略很快就會導致類似的問題 - 如上所述 - 混淆和脆弱的代碼。

如何與多個插座打交道時的狀態參數的幫助?

您可以爲每個調用傳遞一個不同的狀態對象,每個調用都包含自己的客戶端對象。需要注意的是客戶端獲取從國家,而不是一個成員變量:

//Socket client = this.client; // Don't do this. 
Socket client = state.workSocket; 

如果您發現MSDN文檔中的所有其他方法需要客戶端的參數。由於方法簽名是固定的,狀態只是一種傳遞參數的方法。


更新:關於你使用正確的客戶端對象,如果沒有拋出ArgumentException的意見,.NET檢查你的問題。從.net反射反編譯EndReceive我們看到:

if ((result == null) || (result.AsyncObject != this)) 
{ 
    throw new ArgumentException(SR.GetString("net_io_invalidasyncresult"), "asyncResult"); 
} 
+0

而程序員負責確保包含在狀態對象的插座是一樣的Socket對象從'BeginReceive'叫,是否正確?我猜如果他們不同,那會導致錯誤。 – IVlad 2010-03-27 10:59:11

+0

這不是必需的 - .NET保證即使您同時有多個呼叫,也能可靠地傳輸狀態。當你調用一個普通的函數時,你不必檢查你收到的參數是否與調用者用來呼叫你的參數相同。如果你這樣做了,能夠傳遞參數將沒有實際價值,因爲無論如何您都必須參考原始對象才能比較它們。 – 2010-03-27 11:05:54

+0

這不是我的意思。例如,考慮這個:'BeginReceive'回調函數內的'Socket client = state.workSocket;'。考慮在執行'state.workSocket = B'後,名爲'BeginReceive'的套接字'A'傳遞給'StateObject狀態'。這隻有在「A == B」時纔有效,對嗎?我有點困惑,因爲所有的例子都將相同的套接字傳遞給用來調用receive方法的狀態,但是沒有一個明確地說這兩個套接字必須是相同的。我認爲他們應該,但我想確保情況是這樣的。 – IVlad 2010-03-27 11:13:35

1

怎麼能狀態參數幫助時 多個插座打交道?

我想這裏的誤解是什麼狀態參數是確切的;打電話時:

client.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0, 
    ReceiveCallback, state); 

當然,在現場你撥打電話很清楚是什麼「客戶」的調用時,但是當ReceiveCallback方法被調用,該方法無法知道客戶端的方式。請注意,不同套接字上的許多接收可能同時處於運行狀態,而所有共享相同的回調處理程序來處理結果。

將數據傳遞給state參數時,您有機會將其他上下文傳遞給回調函數,以便可以計算出(在本例中)哪個套接字的接收已啓動。

如果在狀態參數傳遞「錯誤值」,那麼就沒有什麼明顯的回調可以做,以保護你......後果取決於你的狀態的數據做什麼。最好的情況是,如果狀態沒有被真正使用,最壞的情況是數據可能會被處理,就好像它來自錯誤的套接字,並且最終可能會在上下文中執行'刪除所有文章'命令沒有提出請求的帳戶。但是,這是沒有任何其他的編程錯誤不同;)