如何停止从 NetworkStream 读取?
How to stop reading from NetworkStream?
我正在尝试从 NetworkStream 读取数据。我写下一个代码:
Imports System.Net
Imports System.Net.Sockets
Public Class Form1
Dim tcpclnt1 As New TcpClient
Dim streamTcp1 As NetworkStream
Dim DataBuffer(1024) As Byte 'Buffer for reading
Dim numberOfBytes As Integer
' event for reading data from stream
Dim evtDataArrival As New AsyncCallback(AddressOf DataProcessing)
Private Sub Btn_Connect5000_Click(sender As Object, e As EventArgs)_
Handles Btn_Connect5000.Click
' Connecting to server
tcpclnt1 = New TcpClient
tcpclnt1.Connect("192.168.1.177", 5000)
streamTcp1 = tcpclnt1.GetStream() 'Create stream for current tcpClient
' HERE WE START TO READ
streamTcp1.BeginRead(DataBuffer, 0, DataBuffer.Length, evtDataArrival, Nothing)
End Sub
Public Sub DataProcessing(ByVal dr As IAsyncResult)
numberOfBytes = streamTcp1.EndRead(dr) 'END READ
' ...HERE SOME ROUTINE FOR PRINT DATA TO TEXTBOXES...
'START NEW READING
streamTcp1.BeginRead(DataBuffer, 0, DataBuffer.Length, evtDataArrival, Nothing)
End Sub
Private Sub Btn_Disconnect5000_Click(sender As Object, e As EventArgs)_
Handles Btn_Disconnect5000.Click
' Disconnect from port 5000. Close TcpClient
streamTcp1.Dispose()
streamTcp1.EndRead(Nothing) 'And here mistake appears !!!
streamTcp1.Close()
tcpclnt1.Close()
End Sub
End Class
问题: 我创建了新客户端和新流。据我所知,通过使用 BeginRead
它开始在新线程中读取数据。所以为了实时数据,我在 DataProcessing
函数的末尾开始新的 BeginRead
。但是我在尝试断开连接时遇到了问题(查看 Btn_Disconnect5000_Click
函数):我尝试关闭流和客户端,但它仍然尝试读取 DataProcessing
方法并说我:
Cannot access a disposed object
(感谢djv的正确翻译!)
所以我想我需要先停止线程,但不知道该怎么做:我尝试了 Dispose()
方法,先尝试关闭流,但仍然不能。我还尝试手动调用 EndRead
方法,但无法理解我必须将其作为参数(参数)分配给它的内容。
EndRead
实际上并没有停止异步读取。它获取读取的字节数,结束 当前 读取操作并抛出操作期间可能发生的任何异常。因此调用它不会阻止进一步的异步操作发生。
我建议您在回调中插入一个空检查,这将退出该方法并停止 BeginRead
被调用更多。
Public Sub DataProcessing(ByVal dr As IAsyncResult)
If streamTcp1 Is Nothing Then _
Return 'Stream is disposed, stop any further reading.
numberOfBytes = streamTcp1.EndRead(dr)
正如 the_lotus 所说,您可能还需要添加异常处理,因为如果您足够不幸,streamTcp1
可能会在通过 null 检查后被处置。
您应该在回调中检查流的原因是,正如我们所讨论的,在 "closing" TCP 连接之后,它进入 CLOSE_WAIT
、TIME_WAIT
或 FIN_*
状态,以便 OS 仍然可以将 late/retransmitted 数据包映射到它。
NetworkStream.BeginRead()
最终调用 WSARecv
在本机端注册一个异步操作,因此它不知道你的套接字是否被处理,因为它只关心连接是否处于活动状态(CLOSE_WAIT
和 TIME_WAIT
连接仍然被认为是活跃的 OS)。这可能会导致回调被调用,即使 socket/stream 已被释放。
不调用 EndRead
直接关闭套接字。
在您调用 EndRead
的那一刻,您正在阻止套接字读取操作,如 Microsoft 的文档所述:
This method blocks until the I/O operation has completed.
您还在 EndRead
之前调用了方法 Dispose
,它释放了 Stream 使用的所有资源,这就是您出现该异常的原因。
考虑只关闭套接字,这将释放套接字正在使用的所有资源,就像这样:
Private Sub Btn_Disconnect5000_Click(sender As Object, e As EventArgs)_
tcpclnt1.Close()
End Sub
升级: 考虑捕获方法 DataProcessing
中 EndRead
的异常。在您关闭套接字的那一刻,将引发 System.ObjectDisposedException
并且异常消息是 无法访问已处置的对象。。发生这种情况是因为您在 EndRead
中阻塞了套接字,而在调用 tcpclnt1.Close()
时它已解除阻塞并释放套接字的所有资源(已处置)并引发异常。
所以你的 DataProcessing
应该是这样的:
Public Sub DataProcessing(ByVal dr As IAsyncResult)
Try
numberOfBytes = streamTcp1.EndRead(dr) 'END READ
' ...HERE SOME ROUTINE FOR PRINT DATA TO TEXTBOXES...
'START NEW READING
streamTcp1.BeginRead(DataBuffer, 0, DataBuffer.Length, evtDataArrival, Nothing)
Catch ex As Exception
System.Console.WriteLine(ex.Message)
End Try
End Sub
我正在尝试从 NetworkStream 读取数据。我写下一个代码:
Imports System.Net
Imports System.Net.Sockets
Public Class Form1
Dim tcpclnt1 As New TcpClient
Dim streamTcp1 As NetworkStream
Dim DataBuffer(1024) As Byte 'Buffer for reading
Dim numberOfBytes As Integer
' event for reading data from stream
Dim evtDataArrival As New AsyncCallback(AddressOf DataProcessing)
Private Sub Btn_Connect5000_Click(sender As Object, e As EventArgs)_
Handles Btn_Connect5000.Click
' Connecting to server
tcpclnt1 = New TcpClient
tcpclnt1.Connect("192.168.1.177", 5000)
streamTcp1 = tcpclnt1.GetStream() 'Create stream for current tcpClient
' HERE WE START TO READ
streamTcp1.BeginRead(DataBuffer, 0, DataBuffer.Length, evtDataArrival, Nothing)
End Sub
Public Sub DataProcessing(ByVal dr As IAsyncResult)
numberOfBytes = streamTcp1.EndRead(dr) 'END READ
' ...HERE SOME ROUTINE FOR PRINT DATA TO TEXTBOXES...
'START NEW READING
streamTcp1.BeginRead(DataBuffer, 0, DataBuffer.Length, evtDataArrival, Nothing)
End Sub
Private Sub Btn_Disconnect5000_Click(sender As Object, e As EventArgs)_
Handles Btn_Disconnect5000.Click
' Disconnect from port 5000. Close TcpClient
streamTcp1.Dispose()
streamTcp1.EndRead(Nothing) 'And here mistake appears !!!
streamTcp1.Close()
tcpclnt1.Close()
End Sub
End Class
问题: 我创建了新客户端和新流。据我所知,通过使用 BeginRead
它开始在新线程中读取数据。所以为了实时数据,我在 DataProcessing
函数的末尾开始新的 BeginRead
。但是我在尝试断开连接时遇到了问题(查看 Btn_Disconnect5000_Click
函数):我尝试关闭流和客户端,但它仍然尝试读取 DataProcessing
方法并说我:
Cannot access a disposed object
(感谢djv的正确翻译!)
所以我想我需要先停止线程,但不知道该怎么做:我尝试了 Dispose()
方法,先尝试关闭流,但仍然不能。我还尝试手动调用 EndRead
方法,但无法理解我必须将其作为参数(参数)分配给它的内容。
EndRead
实际上并没有停止异步读取。它获取读取的字节数,结束 当前 读取操作并抛出操作期间可能发生的任何异常。因此调用它不会阻止进一步的异步操作发生。
我建议您在回调中插入一个空检查,这将退出该方法并停止 BeginRead
被调用更多。
Public Sub DataProcessing(ByVal dr As IAsyncResult)
If streamTcp1 Is Nothing Then _
Return 'Stream is disposed, stop any further reading.
numberOfBytes = streamTcp1.EndRead(dr)
正如 the_lotus 所说,您可能还需要添加异常处理,因为如果您足够不幸,streamTcp1
可能会在通过 null 检查后被处置。
您应该在回调中检查流的原因是,正如我们所讨论的,在 "closing" TCP 连接之后,它进入 CLOSE_WAIT
、TIME_WAIT
或 FIN_*
状态,以便 OS 仍然可以将 late/retransmitted 数据包映射到它。
NetworkStream.BeginRead()
最终调用 WSARecv
在本机端注册一个异步操作,因此它不知道你的套接字是否被处理,因为它只关心连接是否处于活动状态(CLOSE_WAIT
和 TIME_WAIT
连接仍然被认为是活跃的 OS)。这可能会导致回调被调用,即使 socket/stream 已被释放。
不调用 EndRead
直接关闭套接字。
在您调用 EndRead
的那一刻,您正在阻止套接字读取操作,如 Microsoft 的文档所述:
This method blocks until the I/O operation has completed.
您还在 EndRead
之前调用了方法 Dispose
,它释放了 Stream 使用的所有资源,这就是您出现该异常的原因。
考虑只关闭套接字,这将释放套接字正在使用的所有资源,就像这样:
Private Sub Btn_Disconnect5000_Click(sender As Object, e As EventArgs)_
tcpclnt1.Close()
End Sub
升级: 考虑捕获方法 DataProcessing
中 EndRead
的异常。在您关闭套接字的那一刻,将引发 System.ObjectDisposedException
并且异常消息是 无法访问已处置的对象。。发生这种情况是因为您在 EndRead
中阻塞了套接字,而在调用 tcpclnt1.Close()
时它已解除阻塞并释放套接字的所有资源(已处置)并引发异常。
所以你的 DataProcessing
应该是这样的:
Public Sub DataProcessing(ByVal dr As IAsyncResult)
Try
numberOfBytes = streamTcp1.EndRead(dr) 'END READ
' ...HERE SOME ROUTINE FOR PRINT DATA TO TEXTBOXES...
'START NEW READING
streamTcp1.BeginRead(DataBuffer, 0, DataBuffer.Length, evtDataArrival, Nothing)
Catch ex As Exception
System.Console.WriteLine(ex.Message)
End Try
End Sub