【发布时间】:2014-05-08 22:46:42
【问题描述】:
如果我的问题过于冗长,我提前道歉。我查看了“如何使用另一个类的线程接收到的消息来更新 GUI 中的数据?”这个问题。它非常接近我想要做的,但答案不够详细,无法提供帮助。
我已将 VB6 应用程序转换为 VB.NET (VS2013)。该应用程序的主要功能是将查询发送到 Linux 服务器并在调用表单上显示结果。由于 WinSock 控件不再存在,我创建了一个类来处理与 TcpClient 类关联的函数。我可以成功连接到服务器并发送和接收数据。
问题是我有多个表单使用此类将查询消息发送到服务器。服务器响应要在调用表单上显示的数据。当我尝试更新窗体上的控件时,我收到错误“跨线程操作无效:控件 x 从创建它的线程以外的线程访问”。我知道我应该使用 Control.InvokeRequired 和 Control.Invoke 来更新 Main/UI 线程上的控件,但是我在 VB 中找不到一个好的、完整的示例。另外,我有超过 50 个表单,每个表单上都有各种控件,我真的不想为每个控件编写委托处理程序。我还应该提到线程和委托的概念对我来说是非常新的。在过去的一两周里,我一直在阅读我能找到的关于这个主题的所有内容,但我仍然被困住了!
有什么方法可以切换回主线程吗?如果没有,有没有办法我可以只使用一次 Control.Invoke 来覆盖多个控件?
在开始发送和接收数据之前,我尝试在连接后启动一个线程,但是 netStream.BeginRead 在回调函数触发后启动它自己的线程。我也尝试使用 Read 而不是 BeginRead。如果响应中存在大量数据,则效果不佳,BeginRead 处理得更好。我觉得多萝西被困在奥兹国了,我只想回到主线!
提前感谢您提供的任何帮助。
Option Explicit On
Imports System.Net
Imports System.Net.Sockets
Imports System.Text
Imports System.Threading
Friend Class ATISTcpClient
Public Event Receive(ByVal data As String)
Private Shared WithEvents oRlogin As TcpClient
Private netStream As NetworkStream
Private BUFFER_SIZE As Integer = 8192
Private DataBuffer(BUFFER_SIZE) As Byte
Public Sub Connect()
Try
oRlogin = New Net.Sockets.TcpClient
Dim localIP As IPAddress = IPAddress.Parse(myIPAddress)
Dim localPrt As Int16 = myLocalPort
Dim ipLocalEndPoint As New IPEndPoint(localIP, localPrt)
oRlogin = New TcpClient(ipLocalEndPoint)
oRlogin.NoDelay = True
oRlogin.Connect(RemoteHost, RemotePort)
Catch e As ArgumentNullException
Debug.Print("ArgumentNullException: {0}", e)
Catch e As Net.Sockets.SocketException
Debug.Print("SocketException: {0}", e)
End Try
If oRlogin.Connected() Then
netStream = oRlogin.GetStream
If netStream.CanRead Then
netStream.BeginRead(DataBuffer, 0, BUFFER_SIZE, _
AddressOf DataArrival, DataBuffer)
End If
Send(vbNullChar)
Send(User & vbNullChar)
Send(User & vbNullChar)
Send(Term & vbNullChar)
End If
End Sub
Public Sub Send(newData As String)
On Error GoTo send_err
If netStream.CanWrite Then
Dim sendBytes As [Byte]() = Encoding.UTF8.GetBytes(newData)
netStream.Write(sendBytes, 0, sendBytes.Length)
End If
Exit Sub
send_err:
Debug.Print("Error in Send: " & Err.Number & " " & Err.Description)
End Sub
Private Sub DataArrival(ByVal dr As IAsyncResult)
'This is where it switches to a WorkerThread. It never switches back!
On Error GoTo dataArrival_err
Dim myReadBuffer(BUFFER_SIZE) As Byte
Dim myData As String = ""
Dim numberOfBytesRead As Integer = 0
numberOfBytesRead = netStream.EndRead(dr)
myReadBuffer = DataBuffer
myData = myData & Encoding.ASCII.GetString(myReadBuffer, 0, numberOfBytesRead)
Do While netStream.DataAvailable
numberOfBytesRead = netStream.Read(myReadBuffer, 0, myReadBuffer.Length)
myData = myData & Encoding.ASCII.GetString(myReadBuffer, 0, numberOfBytesRead)
Loop
'Send data back to calling form
RaiseEvent Receive(myData)
'Start reading again in case we don‘t have the entire response yet
If netStream.CanRead Then
netStream.BeginRead(DataBuffer, 0,BUFFER_SIZE,AddressOf DataArrival,DataBuffer)
End If
Exit Sub
dataArrival_err:
Debug.Print("Error in DataArrival: " & err.Number & err.Description)
End Sub
【问题讨论】:
-
您可能想查看Asynchronous Programming。吹毛求疵:
On Error GoTo应转换为Try...Catch,Dim myReadBuffer(BUFFER_SIZE) As Byte分配的数组元素比您想要的多一个(应该是BUFFER_SIZE - 1)。 -
我决定将我的答案移至评论,因为它主要是一个链接而不是特定信息。如果代码是一种形式,那么可以,使用
InvokeRequired和Invoke。以下是如何逐步构建解决方案的完整说明:vbforums.com/… 如果代码不在表单中,则您无权访问这些成员。在这种情况下,您应该使用SynchronizationContext类。上面的链接提供了一个在以后的帖子中使用的示例。
标签: vb.net tcpclient beginread