2017-03-28 1 views
0

I utilisé le code suivant dans VB6.0 pour objet Winsock tcpclient:analogique tcpClient.State dans vb.net (contre VB6.0)

Dim SConst(10) As String 
Dim st As Integer 

SConst(0) = "Closed" 
SConst(1) = "Open" 
SConst(2) = "Listening" 
SConst(3) = "ConnectionPending" 
SConst(4) = "ResolvingHost" 
SConst(5) = "HostResolved" 
SConst(6) = "Connecting" 
SConst(7) = "Connected" 
SConst(8) = "Closing" 
SConst(9) = "Error" 
st = tcpClient.state 
TextBox1.Text = SConst(st) 

Maintenant Im using vb.net et que vous voulez faire quelque chose même. Mais il n'y a pas de méthode .state maintenant pour TcpClient objets! Il n'y a que cpClient.Connected mais il renvoie Boolean donc seulement oui ou non. Comment puis-je le faire comme VB6.0?


En utilisant Visual Vinsent's answer je fait ceci:

Public Class Form1 
    Dim status1 As String 
    Dim status2 As String 

Private Sub Btn_Connect5001_Click(sender As Object, e As EventArgs)_ 
        Handles Btn_Connect5001.Click 
     ' Create TcpClient and Connect to 
     tcpclnt2 = New TcpClient  
     Try 
      tcpclnt2.Connect("192.168.1.177", 5001) 
     Catch 
     End Try 
    End Sub 

    Private Sub Btn_Disconnect5001_Click(sender As Object, e As EventArgs)_ 
        Handles Btn_Disconnect5001.Click 
     ' Close TcpClient 
     tcpclnt2.Close() 
    End Sub 

Private Sub Timer1_Tick(sender As Object, e As EventArgs) Handles Timer1.Tick 
     ' Check status every 300ms 
     status2= New IPEndPoint(IPAddress.Parse("192.168.1.177"),5001).GetStatus().ToString() 

     TextBox1.Text = Dns.GetHostName + Environment.NewLine + 
      "port 1 " + status2 + Environment.NewLine 
    End Sub 
End Class 

Le problème est: Au début status2 est "inconnu", si je me connecte pour la première fois status2 est "établie". Si je déconnecte c'est devenu "TimeWait". Mais si je me connecte une fois de plus, il reste "TimeWait". Il ne change jamais sa valeur alors.

Répondre

2

Ni le TcpClient ni son Socket sous-jacent ne semblent avoir une telle implémentation, j'ai donc écrit trois méthodes d'extension qui vous l'obtiendront. Ce qu'ils font est qu'ils itèrent chaque connexion TCP active sur l'ordinateur en utilisant le IPGlobalProperties class et son GetActiveTcpConnections() method, puis ils correspondent à votre connexion contre l'adresse IP locale et distante de TcpClient.

Extensions.vb

Imports System.Runtime.CompilerServices 
Imports System.Reflection 
Imports System.Net 
Imports System.Net.Sockets 
Imports System.Net.NetworkInformation 

Public Module Extensions 
    ''' <summary> 
    ''' Dynamically gets an object's property by name. 
    ''' </summary> 
    ''' <param name="Obj">The object which's property to get.</param> 
    ''' <param name="PropertyName">The name of the property to get.</param> 
    <Extension()> _ 
    Public Function GetProperty(ByVal Obj As Object, ByVal PropertyName As String) As Object 
     Return Obj.GetType().InvokeMember(PropertyName, _ 
              BindingFlags.GetProperty _ 
              Or BindingFlags.IgnoreCase _ 
              Or BindingFlags.Public _ 
               Or BindingFlags.NonPublic _ 
               Or BindingFlags.Instance _ 
               Or BindingFlags.Static, _ 
              Nothing, Obj, Nothing) 
    End Function 

    ''' <summary> 
    ''' Gets the status of a TCP connection. 
    ''' </summary> 
    ''' <param name="Client">The TcpClient which's status to get.</param> 
    ''' <remarks></remarks> 
    <Extension()> _ 
    Public Function GetStatus(ByVal Client As TcpClient) As TcpState 
     If Client Is Nothing OrElse Client.Client Is Nothing Then Return TcpState.Unknown 
     For Each TcpConnection As TcpConnectionInformation In IPGlobalProperties.GetIPGlobalProperties().GetActiveTcpConnections() 

      If TcpConnection.LocalEndPoint.Equals(Client.Client.LocalEndPoint) AndAlso _ 
       TcpConnection.RemoteEndPoint.Equals(Client.Client.RemoteEndPoint) Then 
       Return TcpConnection.State 
      End If 

     Next 
     Return TcpState.Unknown 
    End Function 

    ''' <summary> 
    ''' Gets the status of a TCP connection. 
    ''' </summary> 
    ''' <param name="EndPoint">The IPEndPoint (IP-address) of the TCP connection which's status to get.</param> 
    ''' <remarks></remarks> 
    <Extension()> _ 
    Public Function GetStatus(ByVal EndPoint As IPEndPoint) As TcpState 
     If EndPoint Is Nothing Then Return TcpState.Unknown 
     For Each TcpConnection As TcpConnectionInformation In IPGlobalProperties.GetIPGlobalProperties().GetActiveTcpConnections() 

      If TcpConnection.LocalEndPoint.Equals(EndPoint) OrElse _ 
       TcpConnection.RemoteEndPoint.Equals(EndPoint) Then 
       Return TcpConnection.State 
      End If 

     Next 
     Return TcpState.Unknown 
    End Function 

    ''' <summary> 
    ''' Gets the status of a TCP listener. 
    ''' </summary> 
    ''' <param name="Listener">The TcpListener which's status to get.</param> 
    ''' <remarks></remarks> 
    <Extension()> _ 
    Public Function GetStatus(ByVal Listener As TcpListener) As TcpState 
     If Listener Is Nothing OrElse Listener.Server Is Nothing Then Return TcpState.Unknown 

     'Per the source code, active listeners will always be in the "Listen" state: 
     'https://referencesource.microsoft.com/#System/net/System/Net/NetworkInformation/SystemIPGlobalProperties.cs,51fa569e558be704 
     If Listener.GetProperty("Active") = True Then Return TcpState.Listen 

     Return DirectCast(Listener.LocalEndpoint, IPEndPoint).GetStatus() 'The listener is not in an active state. 
    End Function 
End Module 

Maintenant, pour obtenir le statut de connexion que vous avez trois options:

  1. Obtenir le statut de TcpClient:

    Dim Status As String = yourClient.GetStatus().ToString() 
    

  1. Obtenir le statut de TcpListener:

    Dim Status As String = yourListener.GetStatus().ToString() 
    

  1. Obtenir le statut de connexion avec l'adresse IP spécifiée:

    Dim Status As String = New IPEndPoint(IPAddress.Parse("your IP-address here"), yourPortHere).GetStatus().ToString() 
    

Remarques importantes:

  • L'appel ToString() est nécessaire si vous voulez que le nom de l'état (par exemple Established), sinon vous obtiendrez sa valeur d'énumération qui est juste une Integer normale (par exemple 3).Obtenir l'état d'un TcpClient ou TcpListener ne fonctionnera que si ni eux ni leur Sockets sous-jacent sont éliminés. Si vous souhaitez obtenir le statut d'un déconnectéTcpClient/-Listener vous devez utiliser option no. 3 et l'obtenir par son adresse IP exacte et le port.

    • Les connexions TCP déconnectées peuvent également être intéressantes car elles existent encore après avoir été fermées et ont un état pendant un certain temps. Des exemples d'états déconnectés sont CloseWait ou TimeWait.

En savoir plus:


EDIT:

Pour résoudre le problème avec les connexions persistantes dans un état CLOSE_WAIT ou TIME_WAIT vous pouvez définir le sous-jacent Socket de s'attarder pendant 0 secondes, puis le jeter. Cela provoquera le soi-disant "hard close" en envoyant un RST (réinitialiser) le paquet au lieu de FIN (FIN est ce qui indique à un socket que la connexion est fermée et qu'il devrait aller dans CLOSE-/TIME_WAIT). Cette méthode force la connexion à se fermer et aucun paquet en retard/retransmis ne sera mappé à votre application après cela.

Il est en fait mentionné sur la MSDN documentation:

Si le membre l_onoff de la structure linger est non nul et l_linger membre est nul, closesocket n'est pas bloqué même si les données en file d'attente n'a pas encore été envoyé ou reconnu. Cela s'appelle une fermeture difficile ou avortée, car le circuit virtuel du socket est réinitialisé immédiatement et toutes les données non envoyées sont perdues. Sous Windows, tout appel recv sur le côté distant du circuit échouera avec WSAECONNRESET.

Cela peut être fait dans une méthode simple d'extension (mettre ce dans le module Extensions):

''' <summary> 
''' Forces the specified TcpClient to close without sending FIN to the other endpoint. This will stop the connection from going into a TIME_WAIT, CLOSE_WAIT or FIN_* state. 
''' </summary> 
''' <param name="Client">The TcpClient to close.</param> 
''' <remarks></remarks> 
<Extension()> _ 
Public Sub ForceClose(ByVal Client As TcpClient) 
    Client.Client.LingerState = New LingerOption(True, 0) 'Set the socket to linger, but for 0 seconds (causes the current connection to send an RST-packet and terminate). 
    Client.Client.Dispose() 'Dispose the underlying socket instead of a graceful shutdown. 
    Client.Close() 'Close the TcpClient. 
End Sub 

Ensuite, il suffit d'appeler comme ceci:

yourClient.ForceClose() 
+0

Comme vous le voyez dans Wikipedia article les états TCP sont un peu différents de Winsock de VB6. –

+0

parfait comme toujours! Merci de m'avoir beaucoup aidé! Je ne comprends toujours pas tous les détails donc je le lis maintenant, mais je l'ai déjà essayé et ça marche! Je crée l'objet Timer et vérifie l'état de mon IP et de mes ports actuels (votre méthode 3d). Mais je fais face à un problème: j'ai d'abord une valeur "Unknown", puis "Established" après avoir connecté mon tcpClient. C'est bon. Ensuite, je déconnecte mes clients ('tcpClient.Close()') et obtient "TimeWait". Et maintenant je me connecte à nouveau ('New TcpClient' et' .Connect') mais j'ai quand même des valeurs "TimeWait" (en attendant "Established" à nouveau). Donc, cela ne fonctionne que lors de la première connexion. Qu'est-ce que ça peut être? –

+0

@Mikhail_Sam: Il affiche toujours 'Unknown' quand la connexion n'existe pas. Si une connexion peut être établie alors elle passera généralement l'état 'SynSent' si vite que vous ne le remarquerez pas, d'où la raison pour laquelle elle est' Established' immédiatement après. –