Home > Net >  How to get port a process is listening on in .net?
How to get port a process is listening on in .net?

Time:09-08

As several blogposts and Stackoverflow answers suggest, it is trivial to get this information via a combination of get-process and Get-NetTCPConnection commandlets. It is possible to execute these commands via .net code, parse the output and retrieve the information.

Is it not possible to get the port number a process is listening on in pure .net code using just the .net libraries? System.Diagnostics.Process.GetProcesByName returns a ton of information via the Process object, except for the port the process is listening on.

Any leads highly appreciated.

CodePudding user response:

I had similar task not long ago. Here is complete .NET 6 code that you should be able to adopt for your particular needs:

public static int CheckConnection(string[] servers)
{
    try
    {
        var srvs = servers.ToDictionary(k => k.Split("/", StringSplitOptions.RemoveEmptyEntries)[1], v => false);

        IPGlobalProperties ipProperties = IPGlobalProperties.GetIPGlobalProperties();

        IPEndPoint[] endPoints = ipProperties.GetActiveTcpListeners();
        TcpConnectionInformation[] tcpConnections = ipProperties.GetActiveTcpConnections();

        var count = 0;
        foreach (var info in tcpConnections)
        {
            var ep = string.Format("{0}:{1}", info.RemoteEndPoint.Address, info.RemoteEndPoint.Port);

            if (info.State.ToString().ToUpper() != "ESTABLISHED")
                continue;

            if (srvs.ContainsKey(ep))
            {
                count  ;
                srvs[ep] = true;
            }
        }

        var sb = new StringBuilder();
        foreach (var kvp in srvs)
        {
            sb.AppendFormat("{0,-21}: {1}", kvp.Key, kvp.Value ? "OK" : "FAIL");
            sb.AppendLine();
        }

        Program.logger.Trace($"ZMQF. Connection check:\n{sb}");

        return count;
    }
    catch (Exception ex)
    {
        Program.logger.Fatal(ex, $"ZMQF. CheckConnection exception. Servers: {(string.Join(",", servers))}");
        return -1;
    }
}

CodePudding user response:

Unfortunately, IPGlobalProperties.GetIPGlobalProperties() does not return any information on which process is holding the socket, as it uses GetTcpTable not GetTcpTable2.

You would need to code it yourself. The below code works for TCP over IPv4. You would need similar code for UDP and IPv6.

[DllImport("Iphlpapi.dll", ExactSpelling = true)]
static extern int GetTcpTable2(
  IntPtr TcpTable,
  ref int SizePointer,
  bool Order
);

[StructLayout(LayoutKind.Sequential)]
struct MIB_TCPTABLE
{
    public int dwNumEntries;
}

[StructLayout(LayoutKind.Sequential)]
struct MIB_TCPROW2
{
    public MIB_TCP_STATE dwState;
    public int dwLocalAddr;
    public byte localPort1;
    public byte localPort2;
    // Ports are only 16 bit values (in network WORD order, 3,4,1,2).
    // There are reports where the high order bytes have garbage in them.
    public byte ignoreLocalPort3;
    public byte ignoreLocalPort4;
    public int dwRemoteAddr;
    public byte remotePort1;
    public byte remotePort2;
    // Ports are only 16 bit values (in network WORD order, 3,4,1,2).
    // There are reports where the high order bytes have garbage in them.
    public byte ignoreremotePort3;
    public byte ignoreremotePort4;
    public int dwOwningPid;
    public TCP_CONNECTION_OFFLOAD_STATE dwOffloadState;
}

public enum MIB_TCP_STATE
{
    Closed = 1,
    Listen,
    SynSent,
    SynRcvd,
    Established,
    FinWait1,
    FinWait2,
    CloseWait,
    Closing,
    LastAck,
    TimeWait,
    DeleteTcb
}

enum TCP_CONNECTION_OFFLOAD_STATE
{
    TcpConnectionOffloadStateInHost,
    TcpConnectionOffloadStateOffloading,
    TcpConnectionOffloadStateOffloaded,
    TcpConnectionOffloadStateUploading,
    TcpConnectionOffloadStateMax
}
static List<IPEndPoint> GetSocketsForProcess(int pid, MIB_TCP_STATE state = MIB_TCP_STATE.Established)
{
    const int ERROR_INSUFFICIENT_BUFFER = 0x7A;

    var size = 0;
    var result = GetTcpTable2(IntPtr.Zero, ref size, false);
    if (result != ERROR_INSUFFICIENT_BUFFER)
        throw new Win32Exception(result);
        
    var ptr = IntPtr.Zero;
    try
    {
        ptr = Marshal.AllocHGlobal(size);
        result = GetTcpTable2(ptr, ref size, false);
        if (result != 0)
            throw new Win32Exception(result);

        var list = new List<IPEndPoint>();
        var count = Marshal.ReadInt32(ptr);
        ptr  = Marshal.SizeOf<MIB_TCPTABLE>();
        var length = Marshal.SizeOf<MIB_TCPROW2>();
        for(var i = 0; i < count; i  )
        {
            var row = Marshal.PtrToStructure<MIB_TCPROW2>(ptr);
            if(row.dwOwningPid == pid && row.dwState == state)
                list.Add(new IPEndPoint(row.dwLocalAddr, row.remotePort1 << 8 | row.remotePort2));
            ptr  = length;
        }
        return list;
    }
    finally
    {
        Marshal.FreeHGlobal(ptr);
    }
}
  • Related