listeners icon indicating copy to clipboard operation
listeners copied to clipboard

It's slow when getting info from single port

Open lxl66566 opened this issue 6 months ago • 4 comments

This crate is slow when getting info from single port. It uses get_all() inside the get_processes_by_port function.

fn main() {
    if let Ok(listeners) = listeners::get_processes_by_port(56240) {
        for l in listeners {
            println!("{l}");
        }
    }
}
❯ time cargo run --release
    Finished `release` profile [optimized] target(s) in 0.03s
     Running `target\release\my-test.exe`
PID: 31108      Process name: sing-box.exe
real    0m 16.51s
user    0m 0.01s
sys     0m 0.03s

16s is unacceptable for me.

for comparing:

// AI generated
#include <iphlpapi.h>
#include <psapi.h>
#include <stdio.h>
#include <stdlib.h>
#include <windows.h>
#include <winsock2.h>
#include <ws2tcpip.h>

#pragma comment(lib, "ws2_32.lib")
#pragma comment(lib, "iphlpapi.lib")
#pragma comment(lib, "psapi.lib")

void GetProcessNameAndPidFromPort(DWORD port);

int main(int argc, char *argv[]) {
  if (argc != 2) {
    printf("Usage: %s <port_number>\n", argv[0]);
    return 1;
  }

  DWORD port = atoi(argv[1]);
  GetProcessNameAndPidFromPort(port);

  return 0;
}

void GetProcessNameAndPidFromPort(DWORD port) {
  PMIB_TCPTABLE_OWNER_PID pTcpTable;
  DWORD dwSize = 0;
  DWORD dwRetVal = 0;
  int foundTcp = 0;

  printf("Searching for port %lu in TCP table...\n", port);

  pTcpTable = (PMIB_TCPTABLE_OWNER_PID)malloc(sizeof(MIB_TCPTABLE_OWNER_PID));
  if (pTcpTable == NULL) {
    printf("Error allocating memory for TCP table.\n");
    // Continue to UDP search even if TCP allocation fails
  } else {
    dwSize = sizeof(MIB_TCPTABLE_OWNER_PID);
    if ((dwRetVal = GetExtendedTcpTable(pTcpTable, &dwSize, TRUE, AF_INET,
                                        TCP_TABLE_OWNER_PID_ALL, 0)) ==
        ERROR_INSUFFICIENT_BUFFER) {
      free(pTcpTable);
      pTcpTable = (PMIB_TCPTABLE_OWNER_PID)malloc(dwSize);
      if (pTcpTable == NULL) {
        printf("Error re-allocating memory for TCP table.\n");
      }
    }

    if (pTcpTable != NULL && (dwRetVal = GetExtendedTcpTable(
                                  pTcpTable, &dwSize, TRUE, AF_INET,
                                  TCP_TABLE_OWNER_PID_ALL, 0)) == NO_ERROR) {
      for (DWORD i = 0; i < pTcpTable->dwNumEntries; i++) {
        if (ntohs((u_short)pTcpTable->table[i].dwLocalPort) == port) {
          DWORD pid = pTcpTable->table[i].dwOwningPid;
          HANDLE hProcess = OpenProcess(
              PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, pid);
          if (hProcess != NULL) {
            TCHAR processName[MAX_PATH];
            if (GetModuleFileNameEx(hProcess, NULL, processName, MAX_PATH)) {
              printf("Process for TCP port %lu is: %s (PID: %lu)\n", port,
                     processName, pid);
            } else {
              printf("Could not get process name for TCP (PID: %lu)\n", pid);
            }
            CloseHandle(hProcess);
          } else {
            printf("Could not open process for TCP with PID %lu\n", pid);
          }
          foundTcp = 1;
          break; // Found TCP process, exit loop
        }
      }
      if (!foundTcp) {
        printf("No process found using TCP port %lu\n", port);
      }
    } else if (pTcpTable != NULL) {
      printf("GetExtendedTcpTable failed with error code: %d\n", dwRetVal);
    }

    if (pTcpTable != NULL) {
      free(pTcpTable);
      pTcpTable = NULL;
    }
  }

  // Now search UDP table
  PMIB_UDPTABLE_OWNER_PID pUdpTable;
  int foundUdp = 0;

  printf("\nSearching for port %lu in UDP table...\n", port);

  pUdpTable = (PMIB_UDPTABLE_OWNER_PID)malloc(sizeof(MIB_UDPTABLE_OWNER_PID));
  if (pUdpTable == NULL) {
    printf("Error allocating memory for UDP table.\n");
    return;
  }

  dwSize = sizeof(MIB_UDPTABLE_OWNER_PID);
  if ((dwRetVal = GetExtendedUdpTable(pUdpTable, &dwSize, TRUE, AF_INET,
                                      UDP_TABLE_OWNER_PID, 0)) ==
      ERROR_INSUFFICIENT_BUFFER) {
    free(pUdpTable);
    pUdpTable = (PMIB_UDPTABLE_OWNER_PID)malloc(dwSize);
    if (pUdpTable == NULL) {
      printf("Error re-allocating memory for UDP table.\n");
      return;
    }
  }

  if ((dwRetVal = GetExtendedUdpTable(pUdpTable, &dwSize, TRUE, AF_INET,
                                      UDP_TABLE_OWNER_PID, 0)) == NO_ERROR) {
    for (DWORD i = 0; i < pUdpTable->dwNumEntries; i++) {
      if (ntohs((u_short)pUdpTable->table[i].dwLocalPort) == port) {
        DWORD pid = pUdpTable->table[i].dwOwningPid;
        HANDLE hProcess = OpenProcess(
            PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, pid);
        if (hProcess != NULL) {
          TCHAR processName[MAX_PATH];
          if (GetModuleFileNameEx(hProcess, NULL, processName, MAX_PATH)) {
            printf("Process for UDP port %lu is: %s (PID: %lu)\n", port,
                   processName, pid);
          } else {
            printf("Could not get process name for UDP (PID: %lu)\n", pid);
          }
          CloseHandle(hProcess);
        } else {
          printf("Could not open process for UDP with PID %lu\n", pid);
        }
        foundUdp = 1;
        break; // Found UDP process, exit loop
      }
    }
    if (!foundUdp) {
      printf("No process found using UDP port %lu\n", port);
    }
  } else {
    printf("GetExtendedUdpTable failed with error code: %d\n", dwRetVal);
  }

  if (pUdpTable != NULL) {
    free(pUdpTable);
    pUdpTable = NULL;
  }
}
# gcc test.c -o find_process.exe -lws2_32 -liphlpapi -lpsapi
❯ time ./find_process.exe 56240
Searching for port 56240 in TCP table...
Process for TCP port 56240 is:  ...my-path-to sing-box.exe (PID: 31108)

Searching for port 56240 in UDP table...
No process found using UDP port 56240
real    0m 0.01s
user    0m 0.00s
sys     0m 0.01s

lxl66566 avatar Aug 17 '25 08:08 lxl66566

I believe something may be off with how you're running your test. Try measuring the time it takes to directly run the executable file generated by cargo, without using cargo run. This is what I see from my PC:

Image

GyulyVGC avatar Aug 17 '25 11:08 GyulyVGC

And since you're using Windows, I've verified it also on Windows:

Image

GyulyVGC avatar Aug 17 '25 11:08 GyulyVGC

I think this problem relates to the software running on os: I ran get_all() on my another windows os and counts the time & opened ports.

> Measure-Command { ./target/release/test_rs.exe }

Days              : 0
Hours             : 0
Minutes           : 0
Seconds           : 1
Milliseconds      : 985
Ticks             : 19857842
TotalDays         : 2.29836134259259E-05
TotalHours        : 0.000551606722222222
TotalMinutes      : 0.0330964033333333
TotalSeconds      : 1.9857842
TotalMilliseconds : 1985.7842

❯ cargo r | wc -l
    Finished dev profile [unoptimized] target(s) in 0.09s
     Running target\debug\test_rs.exe
123

and the result on the issue os (which runs sing-box, it will open a lot of ports):

❯ time ./target/release/my-test.exe | wc -l
real    0m 14.74s
user    0m 0.14s
sys     0m 3.87s
1975

so this crate performs bad when there's a lot of opened ports, i think.

lxl66566 avatar Aug 18 '25 06:08 lxl66566

I see, I think you're right.

Well, in theory I could improve the methods to get processes running on a single port, but there's little I can do to improve the time it takes to retrieve them all.

Anyway, thanks for opening this issue making me aware of this.

GyulyVGC avatar Aug 18 '25 06:08 GyulyVGC