2010-03-27 110 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参数时,您有机会将其他上下文传递给回调函数,以便可以计算出(在本例中)哪个套接字的接收已启动。

如果在状态参数传递“错误值”,那么就没有什么明显的回调可以做,以保护你......后果取决于你的状态的数据做什么。最好的情况是,如果状态没有被真正使用,最坏的情况是数据可能会被处理,就好像它来自错误的套接字,并且最终可能会在上下文中执行'删除所有文章'命令没有提出请求的帐户。但是,这是没有任何其他的编程错误不同;)