我对在C#中使用异步套接字方法的正确方法感到困惑.我将参考这两篇文章来解释问题并提出我的问题:MSDN article on asynchronous client sockets和devarticles.com article on socket programming.
我的问题是关于BeginReceive()方法. MSDN文章使用以下两个功能来处理接收数据:
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());
}
}
尽管devarticles.com教程为BeginReceive方法的最后一个参数传递了null,然后继续说明了当我们处理多个套接字时,最后一个参数很有用.现在我的问题是:
>如果仅使用单个套接字,将状态传递给BeginReceive方法有什么意义?是否要避免使用类字段?这样做似乎没有什么意义,但是也许我错过了一些东西.
>当处理多个套接字时,状态参数如何提供帮助?如果我正在调用client.BeginReceive(…),是否会从客户端套接字读取所有数据? devarticles.com教程使其听起来像这样:
m_asynResult = m_socClient.BeginReceive(theSocPkt.dataBuffer,0,theSocPkt.dataBuffer.Length,SocketFlags.None,pfnCallBack,theSocPkt);
数据将从theSocPkt.thisSocket套接字读取,而不是从m_socClient套接字读取.在他们的示例中,两者是相同的,但是如果不是这样会发生什么呢?
我只是没有真正看到最后一个参数在什么地方有用,或者至少对于多个套接字有什么帮助.如果我有多个套接字,我仍然需要在每个套接字上调用BeginReceive,对吗?
解决方法:
What is the point of passing a state to the BeginReceive method if we’re only working with a single socket? Is it to avoid using a class field? It seems like there’s little point in doing it, but maybe I’m missing something.
没错,如果您不使用状态,则必须使用成员.但这比状态变量少局部.本地内容越多,在代码的其他部分进行更改时,它们破坏的可能性就越小.
将其与普通方法调用进行比较.为什么我们不只是将参数设置为成员,然后在不带任何参数的情况下调用所有函数?它可以工作…但是阅读代码太可怕了.通过使范围尽可能地局部化,可以使设计更易于理解和修改.改进的封装导致更强大的代码.
此处同样适用.如果您只有一个异步回调,则可以只在类上设置一个成员就可以摆脱困境,但是如果您有很多此类调用,则此策略将很快导致如上所述的类似问题-令人困惑和脆弱的代码.
How can the state parameter help when dealing with multiple sockets?
您可以为每个调用传递一个不同的状态对象,每个对象都包含自己的客户端对象.请注意,客户端是从状态而不是从成员变量中获取的:
//Socket client = this.client; // Don't do this.
Socket client = state.workSocket;
如果您注意到MSDN文档中的所有其他方法,请将“客户端”作为参数.状态是传递参数的一种方式,因为方法签名是固定的.
更新:关于注释中的问题,.NET检查您是否使用了正确的客户端对象,如果没有,则抛出ArgumentException.通过在.NET Reflector中反编译EndReceive,我们可以看到:
if ((result == null) || (result.AsyncObject != this))
{
throw new ArgumentException(SR.GetString("net_io_invalidasyncresult"), "asyncResult");
}