I used next code in VB6.0 for Winsock object 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)
Now Im using vb.net and want to do something same.
But there is no .state
method now for TcpClient
objects!
There is only cpClient.Connected
but it returns Boolean
so only yes or not. How can I do it alike VB6.0?
Using Visual Vinsent's answer I made this:
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
The problem is: At start status2
is "Unknown", if I connect for the first time status2
is "Established". If I disconnect it became "TimeWait". But If I connect one more time it stay "TimeWait". It never change its value then.
Neither the TcpClient
nor its underlying Socket
appears to have such an implementation, so I wrote three extension methods which will get that for you.
What they do is that they iterate every active TCP connection on the computer using the IPGlobalProperties
class and its GetActiveTcpConnections()
method, then they match your connection against the TcpClient
's local and remote IP-address.
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
Now to get the status of a connection you have three options:
Getting the status of a TcpClient
:
Dim Status As String = yourClient.GetStatus().ToString()
Getting the status of a TcpListener
:
Dim Status As String = yourListener.GetStatus().ToString()
Getting the status of a connection with the specified IP-address:
Dim Status As String = New IPEndPoint(IPAddress.Parse("your IP-address here"), yourPortHere).GetStatus().ToString()
Important notes:
The ToString()
call is necessary if you want the status's name (for example Established
), otherwise you'll get its enumeration value which is just a normal Integer
(for example 3
).
Getting the status of a TcpClient
or TcpListener
will only work if neither they nor their underlying Sockets
are disposed. If you wish to get the status of a disconnected TcpClient/-Listener
you have to use option no. 3 and get it by its exact IP-address and port.
CloseWait
or TimeWait
.Read more:
TCP Protocol Operation - Wikipedia (a list of the different TCP connection states)
EDIT:
To fix the problem with connections lingering in a CLOSE_WAIT
or TIME_WAIT
state you can set the underlying Socket
to linger for 0 seconds and then dispose it. Doing so will cause the socket to perform a so-called "hard close" by sending an RST
(reset) packet instead of FIN
(FIN
is what tells a socket that the connection is closed and that it should go into CLOSE-/TIME_WAIT
). This method forces the connection to close and no late/retransmitted packets will be mapped to your application after that.
It's actually mentioned on the MSDN documentation:
If the
l_onoff
member of the linger structure is nonzero andl_linger
member is zero,closesocket
is not blocked even if queued data has not yet been sent or acknowledged. This is called a hard or abortive close, because the socket's virtual circuit is reset immediately, and any unsent data is lost. On Windows, anyrecv
call on the remote side of the circuit will fail withWSAECONNRESET
.
This can be done in a simple extension method (put this in the Extensions
module):
''' <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
Then just call it like this:
yourClient.ForceClose()