日期:2013-09-15  浏览次数:20512 次

使用异步客户端套接字


异步客户端套接字在等待网络操作完成时不挂起应用程序。相反,它使用标准 .NET 框架异步编程模型在一个线程上处理网络连接,而应用程序继续在原始线程上运行。异步套接字适用于大量使用网络或不能等待网络操作完成才能继续的应用程序。
Socket 类遵循异步方法的 .NET 框架命名模式;例如,同步 Receive 方法对应异步 BeginReceive 和 EndReceive 方法。
异步操作要求回调方法返回操作结果。如果应用程序不需要知道结果,则不需要任何回调方法。本节中的代码示例阐释如何使用某个方法开始与网络设备的连接并使用回调方法结束连接,如何使用某个方法开始发送数据并使用回调方法完成发送,以及如何使用某个方法开始接收数据并使用回调方法结束接收数据。
异步套接字使用多个系统线程池中的线程处理网络连接。一个线程负责初始化数据的发送或接收;其他线程完成与网络设备的连接并发送或接收数据。在下列示例中,System.Threading.ManualResetEvent 类的实例用于挂起主线程的执行并在执行可以继续时发出信号。
在下面的示例中,为了将异步套接字连接到网络设备,Connect 方法初始化 Socket 实例,然后调用 BeginConnect 方法,传递表示网络设备的远程终结点、连接回调方法以及状态对象(即客户端 Socket 实例,用于在异步调用之间传递状态信息)。该示例实现 Connect 方法以将指定的 Socket 实例连接到指定的终结点。它假定存在一个名为 connectDone 的全局 ManualResetEvent
 [C#]
public static void Connect(EndPoint remoteEP, Socket client) {
  client.BeginConnect(remoteEP, 
    new AsyncCallback(ConnectCallback), client );
 
  connectDone.WaitOne();
}

连接回调方法 ConnectCallback 实现 AsyncCallback 委托。它在远程设备可用时连接到远程设备,然后通过设置 ManualResetEvent connectDone 向应用程序线程发出连接完成的信号。下面的代码实现 ConnectCallback 方法。
 [C#]
private static void ConnectCallback(IAsyncResult ar) {
  try {
    // Retrieve the socket from the state object.
    Socket client = (Socket) ar.AsyncState;
 
    // Complete the connection.
    client.EndConnect(ar);
 
    Console.WriteLine("Socket connected to {0}",
      client.RemoteEndPoint.ToString());
 
    // Signal that the connection has been made.
    connectDone.Set();
  } catch (Exception e) {
    Console.WriteLine(e.ToString());
  }
}

Send 示例方法以 ASCII 格式对指定的字符串数据进行编码,并将其异步发送到指定的套接字所表示的网络设备。下面的示例实现 Send 方法。
 [C#]
private static void Send(Socket client, String data) {
  // Convert the string data to byte data using ASCII encoding.
  byte[] byteData = Encoding.ASCII.GetBytes(data);
 
  // Begin sending the data to the remote device.
  client.BeginSend(byteData, 0, byteData.Length, SocketFlags.None,
    new AsyncCallback(SendCallback), client);
}

发送回调方法 SendCallback 实现 AsyncCallback 委托。它在网络设备准备接收时发送数据。下面的示例显示 SendCallback 方法的实现。它假定存在一个名为 sendDone 的全局 ManualResetEvent 实例。
 [C#]
private static void SendCallback(IAsyncResult ar) {
  try {
    // Retrieve the socket from the state object.
    Socket client = (Socket) ar.AsyncState;
 
    // Complete sending the data to the remote device.
    int bytesSent = client.EndSend(ar);
    Console.WriteLine("Sent {0} bytes to server.", bytesSent);
 
    // Signal that all bytes have been sent.
    sendDone.Set();
  } catch (Exception e) {
    Console.WriteLine(e.ToString());
  }
}

从客户端套接字读取数据需要一个在异步调用之间传递值的状态对象。下面的类是用于从客户端套接字接收数据的示例状态对象。它包含以下各项的字段:客户端套接字,用于接收数据的缓冲区,以及用于保存传入数据字符串的 StringBuilder。将这些字段放在该状态对象中,使这些字段的值在多个调用之间得以保留,以便从客户端套接字读取数据。
 [C#]
public class StateObject {
  public Socket workSocket = null;              // Client socket.
  public