Использование асинхронных сокетов клиентаUsing an Asynchronous Client Socket

Асинхронный сокет клиента не приостанавливает работу приложения на то время, пока выполняются сетевые операции.An asynchronous client socket does not suspend the application while waiting for network operations to complete. Вместо этого он использует стандартную модель асинхронного программирования .NET Framework для обработки операций сетевого подключения в одном потоке, пока приложение продолжает работу в изначальном потоке.Instead, it uses the standard .NET Framework asynchronous programming model to process the network connection on one thread while the application continues to run on the original thread. Асинхронные сокеты подходят для приложений, которые интенсивно используют сеть или не могут ждать, пока сетевые операции завершатся.Asynchronous sockets are appropriate for applications that make heavy use of the network or that cannot wait for network operations to complete before continuing.

Класс Socket соответствует шаблону именования платформы .NET Framework для асинхронных методов. Например, синхронный метод Receive соответствует асинхронным методам BeginReceive и EndReceive.The Socket class follows the .NET Framework naming pattern for asynchronous methods; for example, the synchronous Receive method corresponds to the asynchronous BeginReceive and EndReceive methods.

Чтобы вернуть результат, асинхронным операциям требуется метод обратного вызова.Asynchronous operations require a callback method to return the result of the operation. Если приложению не требуется результат, то метод обратного вызова не нужен.If your application does not need to know the result, then no callback method is required. В примере кода в этом разделе демонстрируется использование метода для инициации подключения к сетевому устройству и метода обратного вызова для завершения подключения, метода, который начинает отправку данных, и метода обратного вызова, завершающего отправку, а также метода, который начинает получение данных, и метода обратного вызова, завершающего получение.The example code in this section demonstrates using a method to start connecting to a network device and a callback method to complete the connection, a method to start sending data and a callback method to complete the send, and a method to start receiving data and a callback method to end receiving data.

Асинхронные сокеты используют несколько потоков из системного пула потоков для обработки сетевых подключений.Asynchronous sockets use multiple threads from the system thread pool to process network connections. Один поток отвечает за инициацию отправки или получения данных, а другие завершают подключение к сетевому устройству, а также отправляют или получают данные.One thread is responsible for initiating the sending or receiving of data; other threads complete the connection to the network device and send or receive the data. В приведенном ниже примере экземпляры класса System.Threading.ManualResetEvent приостанавливают выполнение главного потока и сообщают, когда оно может быть продолжено.In the following examples, instances of the System.Threading.ManualResetEvent class are used to suspend execution of the main thread and signal when execution can continue.

В приведенном ниже примере для подключения асинхронного сокета к сетевому устройству метод Connect инициализирует объект Socket, а затем вызывает метод Socket.Connect, передавая удаленную конечную точку, которая представляет сетевое устройство, метод обратного вызова для подключения и объект состояния (объект Socket клиента), который служит для передачи сведений о состоянии между асинхронными вызовами.In the following example, to connect an asynchronous socket to a network device, the Connect method initializes a Socket and then calls the Socket.Connect method, passing a remote endpoint that represents the network device, the connect callback method, and a state object (the client Socket), which is used to pass state information between asynchronous calls. В этом примере реализуется метод Connect для подключения указанного объекта Socket к определенной конечной точке.The example implements the Connect method to connect the specified Socket to the specified endpoint. Предполагается наличие глобального экземпляра класса ManualResetEvent с именем connectDone.It assumes a global ManualResetEvent named connectDone.

Public Shared Sub Connect(remoteEP As EndPoint, client As Socket)  
    client.BeginConnect(remoteEP, _  
       AddressOf ConnectCallback, client)  
  
    connectDone.WaitOne()  
End Sub 'Connect  
public static void Connect(EndPoint remoteEP, Socket client) {  
    client.BeginConnect(remoteEP,
        new AsyncCallback(ConnectCallback), client );  
  
   connectDone.WaitOne();  
}  

Метод обратного вызова подключения ConnectCallback реализует делегат AsyncCallback.The connect callback method ConnectCallback implements the AsyncCallback delegate. Он подключается к удаленному устройству, если оно доступно, а затем сообщает потоку приложения, что подключение завершено, задавая ManualResetEvent connectDone.It connects to the remote device when the remote device is available and then signals the application thread that the connection is complete by setting the ManualResetEvent connectDone. В следующем коде реализуется метод ConnectCallback:The following code implements the ConnectCallback method.

Private Shared Sub ConnectCallback(ar As IAsyncResult)  
    Try  
        ' Retrieve the socket from the state object.  
        Dim client As Socket = CType(ar.AsyncState, Socket)  
  
        ' 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 e As Exception  
        Console.WriteLine(e.ToString())  
    End Try  
End Sub 'ConnectCallback  
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 и отправляет их асинхронно сетевому устройству, представленному указанным сокетом.The example method Send encodes the specified string data in ASCII format and sends it asynchronously to the network device represented by the specified socket. В следующем примере реализуется метод Send:The following example implements the Send method.

Private Shared Sub Send(client As Socket, data As [String])  
    ' Convert the string data to byte data using ASCII encoding.  
    Dim byteData As Byte() = Encoding.ASCII.GetBytes(data)  
  
    ' Begin sending the data to the remote device.  
    client.BeginSend(byteData, 0, byteData.Length, SocketFlags.None, _  
        AddressOf SendCallback, client)  
End Sub 'Send  
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.The send callback method SendCallback implements the AsyncCallback delegate. Он отправляет данные, когда сетевое устройство готово к их приему.It sends the data when the network device is ready to receive. В приведенном ниже примере показана реализация метода SendCallback.The following example shows the implementation of the SendCallback method. Предполагается наличие глобального экземпляра класса ManualResetEvent с именем sendDone.It assumes a global ManualResetEvent named sendDone.

Private Shared Sub SendCallback(ar As IAsyncResult)  
    Try  
        ' Retrieve the socket from the state object.  
        Dim client As Socket = CType(ar.AsyncState, Socket)  
  
        ' Complete sending the data to the remote device.  
        Dim bytesSent As Integer = client.EndSend(ar)  
        Console.WriteLine("Sent {0} bytes to server.", bytesSent)  
  
        ' Signal that all bytes have been sent.  
        sendDone.Set()  
    Catch e As Exception  
        Console.WriteLine(e.ToString())  
    End Try  
End Sub 'SendCallback  
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());  
    }  
}  

Для чтения данных из сокета клиента требуется объект состояния, который передает значения от одного асинхронного вызова к другому.Reading data from a client socket requires a state object that passes values between asynchronous calls. Следующий класс — это пример объекта состояния для получения данных из сокета клиента.The following class is an example state object for receiving data from a client socket. Он содержит поле для сокета клиента, буфер для принимаемых данных и объект StringBuilder для хранения полученной строки данных.It contains a field for the client socket, a buffer for the received data, and a StringBuilder to hold the incoming data string. Добавление этих полей в объект состояния позволяет сохранять их значения между вызовами для чтения данных из сокета клиента.Placing these fields in the state object allows their values to be preserved across multiple calls to read data from the client socket.

Public Class StateObject  
    ' Client socket.  
    Public workSocket As Socket = Nothing
    ' Size of receive buffer.  
    Public BufferSize As Integer = 256  
    ' Receive buffer.  
    Public buffer(256) As Byte
    ' Received data string.  
    Public sb As New StringBuilder()  
End Class 'StateObject  
public class StateObject {  
    // Client socket.  
    public Socket workSocket = null;  
    // Size of receive buffer.  
    public const int BufferSize = 256;  
    // Receive buffer.  
    public byte[] buffer = new byte[BufferSize];  
    // Received data string.  
    public StringBuilder sb = new StringBuilder();  
}  

Метод Receive из примера создает объект состояния, а затем вызывает метод BeginReceive для чтения данных из сокета клиента в асинхронном режиме.The example Receive method sets up the state object and then calls the BeginReceive method to read the data from the client socket asynchronously. В следующем примере реализуется метод Receive:The following example implements the Receive method.

Private Shared Sub Receive(client As Socket)  
    Try  
        ' Create the state object.  
        Dim state As New StateObject()  
        state.workSocket = client  
  
        ' Begin receiving the data from the remote device.  
        client.BeginReceive(state.buffer, 0, state.BufferSize, 0, _  
            AddressOf ReceiveCallback, state)  
    Catch e As Exception  
        Console.WriteLine(e.ToString())  
    End Try  
End Sub 'Receive  
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());  
    }  
}  

Метод обратного вызова получения ReceiveCallback реализует делегат AsyncCallback.The receive callback method ReceiveCallback implements the AsyncCallback delegate. Он получает данные от сетевого устройства и формирует строку сообщения.It receives the data from the network device and builds a message string. Этот метод считывает один или несколько байтов данных из сети в буфер данных, а затем снова вызывает метод BeginReceive, пока данные, отправленные клиентом, не закончатся.It reads one or more bytes of data from the network into the data buffer and then calls the BeginReceive method again until the data sent by the client is complete. После считывания всех данных из клиента ReceiveCallback сообщает потоку приложения о том, что данные закончились, задавая ManualResetEvent sendDone.Once all the data is read from the client, ReceiveCallback signals the application thread that the data is complete by setting the ManualResetEvent sendDone.

В приведенном ниже примере реализуется метод ReceiveCallback.The following example code implements the ReceiveCallback method. В нем предполагается наличие глобальной строковой переменной с именем response, в которой хранится полученная строка, и глобального экземпляра класса ManualResetEvent с именем receiveDone.It assumes a global string named response that holds the received string and a global ManualResetEvent named receiveDone. Для завершения сетевого сеанса сервер должен завершить работу сокета клиента надлежащим образом.The server must shut down the client socket gracefully to end the network session.

Private Shared Sub ReceiveCallback(ar As IAsyncResult)  
    Try  
        ' Retrieve the state object and the client socket
        ' from the asynchronous state object.  
        Dim state As StateObject = CType(ar.AsyncState, StateObject)  
        Dim client As Socket = state.workSocket  
  
        ' Read data from the remote device.  
        Dim bytesRead As Integer = client.EndReceive(ar)  
  
        If bytesRead > 0 Then  
            ' 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, state.BufferSize, 0, _  
                AddressOf ReceiveCallback, state)  
        Else  
            ' All the data has arrived; put it in response.  
            If state.sb.Length > 1 Then  
                response = state.sb.ToString()  
            End If  
            ' Signal that all bytes have been received.  
            receiveDone.Set()  
        End If  
    Catch e As Exception  
        Console.WriteLine(e.ToString())  
    End Try  
End Sub 'ReceiveCallback  
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());  
    }  
}  

См. такжеSee also