How to stop reading from NetworkStream?

大城市里の小女人 提交于 2019-12-14 03:53:05

问题


I'm trying to read data from NetworkStream. I write next code:

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

Problem: I create new client and new stream. By using BeginRead as I understand it starts to read data in new thread. So to do it for real time data, I start new BeginRead at the end of DataProcessing function. But I face the problem when try to disconnect (look at Btn_Disconnect5000_Click function): I try to close stream and client but it sill try to read in DataProcessing method and says me:

Cannot access a disposed object

(Thanks to djv for correct translation!).

So I suppose I need to stop thread first but can't figure out how to do this: I tried Dispose() method, tried close stream first but still can't. Also I tried to call EndRead method manually, but can't understand what I have to assign to it as argument (parameter).


回答1:


EndRead does not actually stop the asynchronous reading. It gets the amount of bytes read, ends the current read operation and throws any exceptions that might have occurred during the operation. Thus calling it will not stop further async operations from happening.

I suggest you insert a null check in the callback instead which will exit the method and stop BeginRead from being called even more.

Public Sub DataProcessing(ByVal dr As IAsyncResult)
    If streamTcp1 Is Nothing Then _
        Return 'Stream is disposed, stop any further reading.

    numberOfBytes = streamTcp1.EndRead(dr)

As the_lotus says you might also need to add exception handling, because if you are unlucky enough streamTcp1 might get disposed after passing the null check.

The reason why you should check the stream in the callback is because, as we discussed, after "closing" a TCP connection it enters CLOSE_WAIT, TIME_WAIT or one of the FIN_* states so that the OS can still map late/retransmitted packets to it.

NetworkStream.BeginRead() ultimately calls WSARecv which registers an asynchronous operation on the native side, thus it is not aware if your socket is disposed or not because all it cares about is if the connection is active (CLOSE_WAIT and TIME_WAIT connections are still considered active to the OS). This may cause the callback to be called even if the socket/stream is disposed.




回答2:


Just close the socket without call the EndRead.

At the moment that you are calling EndRead you are blocking the socket reading operation, as Microsoft's documentation says:

This method blocks until the I/O operation has completed.

Also you are calling the method Dispose, before the EndRead, which free all the resourses used by Stream and that is the reason that you have that exception.

Considere just close the socket, that will release all the resource that the socket is using, just like this:

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

UPGRADE: Considere catch the exception of EndRead that is into the method DataProcessing. At the moment at you are closing the socket a System.ObjectDisposedException will be raise and the exception message is Cannot access a disposed object.. It happens because you are blocking the socket in EndRead and at the moment that tcpclnt1.Close() is invoked it unblocked and release all the resourses of the socket (Disposed) and raise the exception.

So your DataProcessing should looks like:

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


来源:https://stackoverflow.com/questions/43096943/how-to-stop-reading-from-networkstream

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!