Introduction
This article demonstrates how to get the IP address for a remote client using the TcpClient object. There is already a nice solution for this in managed C++ here, but I needed to do this in VB.NET. Unfortunately, I could not get the cast that was used in the C++ solution to work in VB.NET. In addition, there is a much easier way that works in any .NET language!
Background
At TrayGames, we use the TcpListener object to establish a client-server communication for our multi-player online game platform. When a client connects to our server, we use the AcceptTcpClient method to accept the pending request which returns a TcpClient object to us. You could use the AcceptSocket method which gives you greater flexibility, returning a Socket object, but we wanted to use the TcpClient object.
Using the Code
The ClientConnection class encapsulates the TcpClient object. The full implementation of this class handles the sending and receiving of data across the TCP connection, but that's not the point of this article, so that code has been left out. The point of this article is the code that belongs in the PublicIPAddress property, which I'll show in minute. Only the basic members and methods are shown in the class below, though I've left the IDisposable implementation for completeness:
Imports System.Net.Sockets
Imports System.Reflection
Public NotInheritable Class ClientConnection
Implements IDisposable
Private Client As TcpClient
Private PublicIP As String
Public Sub New(ByVal client As TcpClient)
If client Is Nothing Then
Throw New ArgumentNullException("client")
End If
Me.Client = client
End Sub
#Region " IDisposable Support "
Private disposedValue As Boolean Private Sub Dispose(ByVal disposing As Boolean)
If Not Me.disposedValue Then
If disposing Then
If Not Client Is Nothing Then
Client.Close()
Client = Nothing
End If
End If
End If
Me.disposedValue = True
End Sub
Public Sub Dispose() Implements IDisposable.Dispose
Dispose(True)
GC.SuppressFinalize(Me)
End Sub
#End Region
End Class
One of the following properties, which demonstrate two different ways to get the remote IP address, is meant to be added to the ClientConnection class. The easiest way to get the IP address is to get the underlying Socket object through the Client method of the TcpClient object. Once you have the underlying socket, you can make a call to the RemoteEndPoint property which gets the EndPoint that contains the remote IP address and port number to which the Socket is connected. You must cast this EndPoint to an IPEndPoint before retrieving any information. You can then call the IPEndPoint.Address method to retrieve the remote IP address as shown below:
Public ReadOnly Property PublicIPAddress() As String
Get
If PublicIP = String.Empty Then
Try
Dim ipend As Net.IPEndPoint = Client.Client.RemoteEndPoint
If Not ipend Is Nothing Then
PublicIP = ipend.Address.ToString
End If
Catch ex As System.ObjectDisposedException
PublicIP = String.Empty
Catch ex As SocketException
PublicIP = String.Empty
End Try
End If
Return PublicIP
End Get
End Property
The other way to get the underlying Socket object is from a NetworkStream object. This object can be retrieved through the TcpClient object's GetStream method. Unfortunately, the Socket member is not accessible to us directly because it's marked Protected. You could derive your own class from NetworkStream, which is the solution used in the managed C++ article mentioned earlier, but the required cast doesn't seem to work in VB.NET. Instead, we can circumvent this protection by using a little reflection as shown below:
Public ReadOnly Property PublicIPAddress() As String
Get
If PublicIP = String.Empty Then
Try
Dim pi As PropertyInfo = _
client.GetStream.GetType.GetProperty( _
"Socket", BindingFlags.NonPublic Or BindingFlags.Instance)
If Not pi Is Nothing Then
PublicIP = pi.GetValue(client.GetStream, _
Nothing).RemoteEndPoint.ToString.Split(":")(0)
End If
Catch ex As System.Exception
PublicIP = String.Empty
End Try
End If
Return PublicIP
End Get
End Property
Either version of the PublicIPAddress property will work fine. Note that you don't have to have a class that encapsulates the TcpClient object, you only need the TcpClient object itself for either of these techniques. That's all there is to it!
Points of Interest
This article is interesting if you want to learn some creative ways to manipulate the TcpClient object which provides client connections for TCP network services. Although this wasn't the most complicated topic, hopefully, I've saved someone a little time figuring this out! If you are interested in creating your own multi-player online games, you should check out the TGSDK which is downloadable from the TrayGames web site.