2017-03-29 1 views
3

J'essaye de lire des données de NetworkStream. J'écris le code suivant:Comment arrêter de lire depuis 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 

Problème: Créer de nouveaux clients et de nouveaux flux. En utilisant BeginRead si je comprends bien, il commence à lire les données dans un nouveau thread. Donc, pour le faire pour les données en temps réel, je commence BeginRead à la fin de la fonction DataProcessing. Mais je fais face à ce problème lorsque vous essayez de déconnecter (regardez la fonction Btn_Disconnect5000_Click): J'essaie de fermer un flux et le client, mais il SILL essayer de lire dans DataProcessing méthode et me dit:

Cannot access a disposed object

(Merci à DJV pour la traduction correcte !).

Donc je suppose que je dois arrêter le fil d'abord, mais ne peux pas comprendre comment faire ceci: J'ai essayé la méthode Dispose(), j'ai essayé de fermer le flux en premier mais je ne peux toujours pas. J'ai aussi essayé d'appeler la méthode EndRead manuellement, mais je ne peux pas comprendre ce que je dois lui assigner comme argument (paramètre).

+1

cela pourrait peut-être aider: http://stackoverflow.com/questions/11785735/proper-way-to-prematurely-abort-beginread-and-beginwrite –

+2

La traduction anglaise de votre message d'exception est * Impossible d'accéder à un objet éliminé * – djv

+0

Comme le suggère la réponse dans le lien de Lotus, essayez simplement de fermer le 'NetworkStream' au lieu d'appeler' EndRead' (ma réponse explique pourquoi vous n'avez pas besoin d'appeler 'EndRead' là non plus). –

Répondre

2

EndRead ne s'arrête pas en fait la lecture asynchrone. Il obtient la quantité d'octets lus, se termine l'opération de lecture en cours et lance des exceptions qui auraient pu survenir au cours de l'opération. Ainsi, l'appeler n'arrêtera pas d'autres opérations asynchrones.

Je vous suggère d'insérer une vérification null dans le rappel à la place qui quittera la méthode et arrêtera BeginRead d'être appelé encore plus.

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

    numberOfBytes = streamTcp1.EndRead(dr) 

Comme the_lotus dit que vous pourriez aussi avoir besoin d'ajouter la gestion des exceptions, parce que si vous êtes assez streamTcp1 obtiendrez peut-être disposé malchanceux après avoir passé le contrôle nul.

La raison pour laquelle vous devriez vérifier le flux dans le rappel est dû au fait, que nous avons discuté, après « fermeture » d'une connexion TCP entre dans CLOSE_WAIT, TIME_WAIT ou l'un des FIN_* états de telle sorte que le système d'exploitation peut toujours mapper fin/réémis des paquets à lui.

NetworkStream.BeginRead() appelle finalement WSARecv qui enregistre une opération asynchrone sur le côté natif, il est donc pas au courant si votre prise est disposé ou non parce que tout ce qu'il se soucie est de savoir si la connexion est active (CLOSE_WAIT et TIME_WAIT connexions sont toujours considérés comme actifs à l'OS). Cela peut entraîner l'appel du rappel même si le socket/flux est supprimé.

+2

Est-il possible que le flux puisse se terminer entre le If et le EndRead dans de rares cas? On dirait que la seule option est d'attraper l'exception. –

+0

@the_lotus: Je ne pense pas, ou c'est au moins pas très probable ... Le rappel n'est pas soulevé dans le fil courant? Manipulation d'exception là-bas est bien non plus. :) –

+0

@the_lotus: Après le test, il semble que le rappel n'est pas déclenché dans le même thread, donc il pourrait y avoir des problèmes de concurrence comme vous le dites, oui. –

1

juste fermer le socket sans appel le EndRead.

Au moment où vous appelez EndRead vous bloquez l'opération de lecture de prise, que la documentation de Microsoft dit:

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

vous êtes également appel à la méthode Dispose, avant la EndRead, qui libèrent tous les resourses utilisés par Stream et c'est la raison pour laquelle vous avez cette exception.

Considere juste fermer la prise, qui libérera toutes les ressources que la prise utilise, comme ceci:

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

UPGRADE: Considere attraper l'exception de EndRead qui est dans la méthode DataProcessing. Au moment où vous fermez le socket, un System.ObjectDisposedException sera déclenché et le message d'exception est Impossible d'accéder à un objet éliminé.. Cela arrive parce que vous bloquez le socket dans EndRead et au moment où tcpclnt1.Close() est invoqué, il est débloqué et libère tous les resourses du socket (Disposé) et déclenche l'exception.

donc votre DataProcessing devrait ressembler à:

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 
+0

J'ai supprimé 'Dispose()' et 'EndRead' alors maintenant j'utilise' tcpclnt1.Close' mais j'ai toujours la même erreur: TcpClient est déjà fermé mais il essaie toujours d'appeler 'numberOfBytes = streamTcp1.EndRead (dr) 'à l'intérieur de ma fonction' DataProcessing'. –

+0

@Mikhail_Sam J'ai couru votre code pour répéter votre problème et j'ai fait la mise à niveau de ma réponse. Aussi, je n'appelle que le 'tcpclnt1.Close()' pour fermer le socket et libérer toute la ressource. –

+0

Oui, j'ai résolu ce problème en ajoutant l'exception, vous avez raison! Je vous remercie! –