Reputation: 219
Ok, So I am using the MSDN example for asynchronous client sockets. The code I am using for the client is below. I'm just testing it right now by connecting to a telnet server installed on my computer as well as by connecting to the telnet port on a couple of cisco switches. The results are same for connecting to any of those telnet servers. The receiveCallback routine executes... If bytesRead > 0 comes up as true, which then causes client.BeginReceive to be called again. At this point the program hangs for a long time 30 secs or more (I'm assuming it hangs until the telnet server closes the connection). While it is hung the windows form is not responsive (can't even move it around the screen). Eventally, the the ReceiveCallback routine is called again, this time bytesRead > 0 is false and the program unhangs.
I get that I haven't built in logic to deal with messages etc, but why does beginReceive cause the ui to become unresponsive.
code:
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
Upvotes: 1
Views: 1857
Reputation: 108975
All those calls to ManualResetEvent.WaitOne()
will block, and you are calling this on your UI thread (from the click handler), thus your UI blocks.
There is no point performing asynchronous operations and immediately blocking with a wait on the same thread -- you've just re-implemented the synchronous APIs (underneath everything all IO in Windows is asynchronous—the synchronous APIs are just a wrapper that starts an asynchronous operation and then blocks on its completion).
You need to rework your code:
EndXYZ
method to match the BeginXYZ
and get the result or exception on failure) and start the next asynchronous operation.BeginReceive
) use Control.BeginInvoke
to run a callback on the UI thread associated with the control.All except starting the series of operations and the end will take place in the thread pool, not blocking the UI. (Of course the state of the UI might need to reflect this processing, eg. fields locked while the result is being obtained).
This model of programming is rather like hooking a sequence of UI events, it is not about having a single method with a linear sequence of calls (at least, not until the async CTP becomes part of a future revision of VB and C#).
Upvotes: 2