套接字 - 为什么beginRecieve阻止?

时间:2011-01-30 14:46:57

标签: vb.net sockets

好的,我正在使用MSDN示例进行异步客户端套接字。我在客户端使用的代码如下。我现在只是通过连接到我的计算机上安装的telnet服务器以及连接到几个cisco交换机上的telnet端口来测试它。连接到任何这些telnet服务器的结果都相同。 receiveCallback例程执行... If bytesRead> 0出现为true,然后再次调用client.BeginReceive。此时程序会挂起很长时间30秒或更长时间(我假设它会挂起,直到telnet服务器关闭连接)。当它挂起时,窗体形状没有响应(甚至无法在屏幕上移动)。最后,再次调用ReceiveCallback例程,这次是bytesRead> 0为假,程序取消。

我知道我没有内置逻辑来处理消息等,但是为什么beginReceive会导致ui无法响应。

代码:

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 个答案:

答案 0 :(得分:2)

所有对ManualResetEvent.WaitOne()的调用都会阻塞,你在UI线程(来自点击处理程序)上调用它,因此你的UI阻塞。

执行异步操作并立即阻止同一线程上的等待是没有意义的 - 您只是重新实现了同步API(在Windows中所有IO的所有内容都是异步的 - 同步API只是一个包装器,启动异步操作,然后阻止其完成。

您需要重新编写代码:

  1. 在回调中完成操作(EndXYZ方法以匹配BeginXYZ并在失败时获得结果或异常)并开始下一个异步操作。
  2. 在最终回调中(在您的情况下,传递给BeginReceive的回调)使用Control.BeginInvoke在与控件关联的UI线程上运行回调。
  3. 删除所有等待。
  4. 除了开始一系列操作和结束之外的所有操作都将在线程池中进行,而不是阻止UI。 (当然,UI的状态可能需要反映此处理,例如,在获得结果时锁定的字段)。

    这种编程模型更像是挂钩一系列UI事件,它不是关于使用线性调用序列的单一方法(至少,直到异步CTP成为VB和C#的未来版本的一部分) )。