问题
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