сокеты - почему блок beginRecieve? - PullRequest
1 голос
/ 30 января 2011

Хорошо, поэтому я использую пример MSDN для асинхронных клиентских сокетов. Код, который я использую для клиента, приведен ниже. Я просто сейчас тестирую это, подключаясь к серверу telnet, установленному на моем компьютере, а также подключаясь к порту telnet на паре коммутаторов cisco. Результаты одинаковы для подключения к любому из этих серверов telnet. Процедура receiveCallback выполняется ... Если значение bytesRead> 0 соответствует значению true, что приводит к повторному вызову client.BeginReceive. В этот момент программа зависает в течение 30 секунд или более (я предполагаю, что она зависает до тех пор, пока сервер telnet не закроет соединение). Пока он подвешен, форма окна не реагирует (даже не может перемещать его по экрану). В конце концов, процедура ReceiveCallback вызывается снова, на этот раз bytesRead> 0 имеет значение false и программа разворачивается.

Я понял, что у меня нет встроенной логики для работы с сообщениями и т. Д., Но почему beginReceive приводит к тому, что пользовательский интерфейс перестает отвечать на запросы.

код:

Imports System.Net
Imports System.Net.Sockets
Imports System.Text

Imports System

Imports System.Threading

Public Class Form2


    Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
        AsynchronousClient.Main()
    End Sub

End Class





' State object for receiving data from remote device.

Public Class StateObject
    ' Client socket.
    Public workSocket As Socket = Nothing
    ' Size of receive buffer.
    Public Const BufferSize As Integer = 256
    ' Receive buffer.
    Public buffer(BufferSize) As Byte
    ' Received data string.
    Public sb As New StringBuilder
End Class 'StateObject


Public Class AsynchronousClient
    ' The port number for the remote device.
    Private Const port As Integer = 23

    ' ManualResetEvent instances signal completion.
    Private Shared connectDone As New ManualResetEvent(False)
    Private Shared sendDone As New ManualResetEvent(False)
    Private Shared receiveDone As New ManualResetEvent(False)

    ' The response from the remote device.
    Private Shared response As String = String.Empty


    Public Shared Sub Main()
        'http://msdn.microsoft.com/en-us/library/bew39x2a.aspx
        'http://msdn.microsoft.com/en-us/library/bbx2eya8.aspx



        ' Establish the remote endpoint for the socket.
        ' For this example use local machine.
        '  Dim ipHostInfo As IPHostEntry = Dns.Resolve(Dns.GetHostName())
        Dim ipHostInfo As IPHostEntry = Dns.Resolve("127.0.0.1")

        Dim ipAddress As IPAddress = ipHostInfo.AddressList(0)
        Dim remoteEP As New IPEndPoint(ipAddress, port)

        ' Create a TCP/IP socket.
        Dim client As New Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp)

        ' Connect to the remote endpoint.
        client.BeginConnect(remoteEP, New AsyncCallback(AddressOf ConnectCallback), client)

        ' Wait for connect.
        connectDone.WaitOne()

        ' Send test data to the remote device.
        Send(client, "This is a test<EOF>")
        ' Send(client, "GET")
        sendDone.WaitOne()

        ' Receive the response from the remote device.
        Receive(client)
        receiveDone.WaitOne()
        Debug.Print("got here")
        ' Write the response to the console.
        Debug.Print("Response received : " & response)

        ' Release the socket.
        client.Shutdown(SocketShutdown.Both)
        client.Close()
    End Sub 'Main


    Private Shared Sub ConnectCallback(ByVal ar As IAsyncResult)
        ' Retrieve the socket from the state object.
        Dim client As Socket = CType(ar.AsyncState, Socket)

        ' Complete the connection.
        client.EndConnect(ar)

        Debug.Print("Socket connected to " & client.RemoteEndPoint.ToString())

        ' Signal that the connection has been made.
        connectDone.Set()
    End Sub 'ConnectCallback


    Private Shared Sub Receive(ByVal client As Socket)

        ' 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, StateObject.BufferSize, 0, New AsyncCallback(AddressOf ReceiveCallback), state)
    End Sub 'Receive


    Private Shared Sub ReceiveCallback(ByVal ar As IAsyncResult)

        ' 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, StateObject.BufferSize, 0, New AsyncCallback(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
    End Sub 'ReceiveCallback


    Private Shared Sub Send(ByVal client As Socket, ByVal 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, 0, New AsyncCallback(AddressOf SendCallback), client)
    End Sub 'Send


    Private Shared Sub SendCallback(ByVal ar As IAsyncResult)
        ' 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)
        Debug.Print("Sent " & bytesSent & " bytes to server.")

        ' Signal that all bytes have been sent.
        sendDone.Set()
    End Sub 'SendCallback
End Class 'AsynchronousClient

1 Ответ

2 голосов
/ 30 января 2011

Все эти вызовы на ManualResetEvent.WaitOne() будут блокироваться, и вы вызываете это в своем потоке пользовательского интерфейса (из обработчика щелчков), таким образом, ваш пользовательский интерфейс блокируется.

Нет смысла выполнять асинхронные операции и немедленно блокировать с ожиданием в том же потоке - вы только что повторно реализовали синхронные API (под всем, что все в IO в Windows асинхронно - синхронные API - это просто оболочка, которая запускает асинхронную операцию и затем блокирует ее завершение).

Вам необходимо переделать свой код:

  1. В обратных вызовах завершите операцию (метод EndXYZ, соответствующий BeginXYZ и получите результат или исключение при ошибке) и запустите следующую асинхронную операцию.
  2. В последнем обратном вызове (в вашем случае тот, который был передан BeginReceive), используйте Control.BeginInvoke для запуска обратного вызова в потоке пользовательского интерфейса, связанном с элементом управления.
  3. Удалить все ожидания.

Все, кроме запуска серии операций и ее завершения, будет происходить в пуле потоков, не блокируя пользовательский интерфейс. (Конечно, состояние пользовательского интерфейса может отражать эту обработку, например, блокировка полей во время получения результата).

Эта модель программирования скорее похожа на перехват последовательности событий пользовательского интерфейса, она не сводится к наличию единственного метода с линейной последовательностью вызовов (по крайней мере, до тех пор, пока асинхронная CTP-версия не станет частью будущей ревизии VB и C # ).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...