如何停止从NetworkStream中读取?

时间:2017-03-29 14:49:11

标签: .net vb.net sockets io 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它开始在新线程中读取数据。为了实现实时数据,我在BeginRead函数末尾开始新的DataProcessing。但是我在尝试断开连接时遇到问题(查看Btn_Disconnect5000_Click函数):我尝试关闭流和客户端但是它会尝试用DataProcessing方法读取并说:

  

无法访问已处置的对象

(感谢djv正确翻译!)。

所以我想我需要先停止线程,但无法弄清楚如何执行此操作:我尝试Dispose()方法,先尝试关闭流但仍然无法解决。此外,我尝试手动调用EndRead方法,但无法理解我必须将其作为参数(参数)分配给它。

2 个答案:

答案 0 :(得分:2)

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可能会在通过空检查后被处理掉。

您应该检查回调中的流的原因是,正如我们所讨论的那样,在“关闭”TCP连接后,它会输入CLOSE_WAITTIME_WAIT或其中一个FIN_*状态,以便操作系统仍然可以将延迟/重传的数据包映射到它。

NetworkStream.BeginRead()最终调用WSARecv在本机端注册异步操作,因此它不知道您的套接字是否被处置,因为它关心的是连接是否处于活动状态({ {1}}和CLOSE_WAIT连接仍被认为是OS的活动状态)。即使套接字/流被丢弃,这也可能导致调用回调。

答案 1 :(得分:1)

只需关闭套接字而不调用EndRead

当你打电话给EndRead时,你正在阻止套接字读取操作,正如微软的文档所说:

  

此方法将阻塞,直到I / O操作完成。

此外,您在Dispose之前调用方法EndRead,该方法释放了Stream使用的所有资源,这就是您拥有该异常的原因。

考虑关闭套接字,这将释放套接字正在使用的所有资源,就像这样:

Private Sub Btn_Disconnect5000_Click(sender As Object, e As EventArgs)_
    tcpclnt1.Close()
End Sub

升级:请注意方法EndRead中的DataProcessing例外情况。在您关闭套接字时,将引发System.ObjectDisposedException并且异常消息为无法访问已处置的对象。。之所以发生这种情况,是因为您在EndRead中阻塞了套接字,并且在tcpclnt1.Close()被调用时它被解除阻塞并释放套接字的所有资源(Disposed)并引发异常。

所以你的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