vb.net中的tcpClient.State模拟(与vb6.0相比)

vb.net中的tcpClient.State模拟(与vb6.0相比),.net,vb.net,tcp,vb6,vb6-migration,.net,Vb.net,Tcp,Vb6,Vb6 Migration,我在VB6.0中为Winsock对象tcpClient使用了下一个代码: 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) = "Conne

我在VB6.0中为Winsock对象tcpClient使用了下一个代码:

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) 
现在我正在使用vb.net,想做些同样的事情。 但是现在对于
TcpClient
对象没有
.state
方法! 只有
cpClient.Connected
,但它返回
Boolean
,因此只有yes或not。我怎样才能像VB6.0那样做呢


使用我制作的:

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

问题是:开始时
status2
为“未知”,如果我第一次连接
status2
为“已建立”。如果我断开连接,它会变成“TimeWait”。但如果我再连接一次,它将保持“TimeWait”。它永远不会改变它的值。

无论是
TcpClient
还是它的底层
Socket
似乎都没有这样的实现,因此我编写了三种扩展方法,可以为您实现这一点

他们所做的是使用和迭代计算机上的每个活动TCP连接,然后根据
TcpClient
的本地和远程IP地址匹配您的连接

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

  • 获取
    TcpListener
    的状态:

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

  • 获取具有指定IP地址的连接的状态:

    Dim Status As String = New IPEndPoint(IPAddress.Parse("your IP-address here"), yourPortHere).GetStatus().ToString()
    
  • 重要注意事项:

    • 如果需要状态名称(例如
      已建立
      ),则必须调用
      ToString()
      ,否则将获得其枚举值,该值仅为正常的
      整数
      (例如
      3

    • 获取
      TcpClient
      TcpListener
      的状态仅在它们及其底层
      socket
      均未被释放的情况下才有效。如果您希望获取已断开连接的TcpClient/-Listener的状态,则必须使用选项3并通过其确切的IP地址和端口获取

      • 断开的TCP连接实际上也很有趣,因为它们在关闭后仍然存在,并且有一段时间的状态。断开连接状态的示例有
        CloseWait
        TimeWait
    阅读更多信息:

    • (不同TCP连接状态的列表)


    编辑:

    要解决连接在
    CLOSE\u WAIT
    TIME\u WAIT
    状态下延迟的问题,您可以将基础
    Socket
    设置为延迟0秒,然后进行处理。这样做会导致套接字执行所谓的“硬关闭”,即发送
    RST
    (重置)数据包,而不是
    FIN
    FIN
    告诉套接字连接已关闭,它应该进入
    close-/TIME\u WAIT
    )。此方法强制关闭连接,之后不会将延迟/重新传输的数据包映射到应用程序

    事实上,这是在:

    如果linger结构的
    l_onoff
    成员为非零且
    l_linger
    成员为零,则即使队列数据尚未发送或确认,
    closesocket
    也不会被阻止。这称为硬关闭或中止关闭,因为套接字的虚拟电路会立即重置,任何未发送的数据都会丢失。在Windows上,电路远程侧的任何
    recv
    调用都将因
    WSAECONNRESET
    而失败

    这可以通过一种简单的扩展方法来实现(将其放入
    Extensions
    模块中):


    正如您在维基百科文章中看到的,TCP状态与VB6的Winsock.perfect有点不同!谢谢你帮了我这么多!我仍然不明白所有的细节,所以我现在读它,但我已经尝试了这个,它的工作!我创建计时器对象并检查当前IP和端口的状态(您的3d方法)。但我面临一个问题:首先我有“未知”值,然后在连接tcpClient后“建立”。没关系。然后我断开我的客户端(
    tcpClient.Close()
    )并获得“TimeWait”。现在我再次连接(
    newtcpclient
    .connect
    ),但仍然有“TimeWait”值(期望再次“建立”)。所以它只在第一次连接时起作用。可能是什么?@Mikhail_Sam:当连接不存在时,它总是显示
    未知
    。如果可以建立连接,那么它通常会以如此快的速度通过
    SynSent
    状态,你不会注意到它,因此它会立即建立
    SynSent
    。是的,我真的从未看到
    SynSent
    值!我想它太快了。但是为什么在我创建了新的TcpClient之后,
    没有再次建立
    ?@Mikhail_Sam:即使在新的连接之后,它也会显示
    TimeWait
    CloseWait
    ,因为旧的连接(具有相同的IP和端口)仍然存在,并且位于列表的前面。最好的方法是找到一种强制关闭旧连接的方法,否则检查是否存在重复连接并返回最佳选项。
    Dim Status As String = New IPEndPoint(IPAddress.Parse("your IP-address here"), yourPortHere).GetStatus().ToString()
    
    ''' <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
    
    yourClient.ForceClose()