核心思想是调用 WinAPI 中的 GetExtendedTcpTable 方法来获取所有活动的 TCP 连接的信息,包括进程ID等等,主要实现如下:
TcpConnectionTableHelper.cs:
-
-
using System.Collections.Generic;
-
-
using System.Runtime.InteropServices;
-
-
using System.Threading.Tasks;
-
-
namespace TcpConnectionMonitor
-
-
public class TcpConnectionTableHelper
-
-
[DllImport("Ws2_32.dll")]
-
static extern ushort ntohs(ushort netshort);
-
-
[DllImport("iphlpapi.dll", SetLastError = true)]
-
static extern uint GetExtendedTcpTable(IntPtr pTcpTable, ref int dwOutBufLen, bool sort, int ipVersion, TCP_TABLE_TYPE tblClass, int reserved);
-
-
[StructLayout(LayoutKind.Sequential)]
-
public struct MIB_TCPROW_OWNER_PID
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
return BitConverter.ToUInt16(new byte[2] { localPort2, localPort1 }, 0);
-
-
-
-
-
-
-
-
return BitConverter.ToUInt16(new byte[2] { remotePort2, remotePort1 }, 0);
-
-
-
-
-
[StructLayout(LayoutKind.Sequential)]
-
public struct MIB_TCPTABLE_OWNER_PID
-
-
public uint dwNumEntries;
-
MIB_TCPROW_OWNER_PID table;
-
-
-
public static string GetIpAddress(long ipAddrs)
-
-
-
-
System.Net.IPAddress ipAddress = new System.Net.IPAddress(ipAddrs);
-
return ipAddress.ToString();
-
-
catch { return ipAddrs.ToString(); }
-
-
-
-
public static ushort GetTcpPort(int tcpPort)
-
-
return ntohs((ushort)tcpPort);
-
-
-
public static MIB_TCPROW_OWNER_PID[] GetAllTcpConnections()
-
-
MIB_TCPROW_OWNER_PID[] tcpConnectionRows;
-
-
-
-
-
uint ret = GetExtendedTcpTable(IntPtr.Zero, ref buffSize, true, AF_INET, TCP_TABLE_TYPE.TCP_TABLE_OWNER_PID_ALL, 0);
-
if (ret != 0 && ret != 122)
-
-
throw new Exception("Error occurred when trying to query tcp table, return code: " + ret);
-
-
IntPtr buffTable = Marshal.AllocHGlobal(buffSize);
-
-
-
-
ret = GetExtendedTcpTable(buffTable, ref buffSize, true, AF_INET, TCP_TABLE_TYPE.TCP_TABLE_OWNER_PID_ALL, 0);
-
-
-
throw new Exception("Error occurred when trying to query tcp table, return code: " + ret);
-
-
-
-
MIB_TCPTABLE_OWNER_PID table = (MIB_TCPTABLE_OWNER_PID)Marshal.PtrToStructure(buffTable, typeof(MIB_TCPTABLE_OWNER_PID));
-
IntPtr rowPtr = (IntPtr)((long)buffTable + Marshal.SizeOf(table.dwNumEntries));
-
tcpConnectionRows = new MIB_TCPROW_OWNER_PID[table.dwNumEntries];
-
-
for (int i = 0; i < table.dwNumEntries; i++)
-
-
MIB_TCPROW_OWNER_PID tcpRow = (MIB_TCPROW_OWNER_PID)Marshal.PtrToStructure(rowPtr, typeof(MIB_TCPROW_OWNER_PID));
-
tcpConnectionRows[i] = tcpRow;
-
rowPtr = (IntPtr)((long)rowPtr + Marshal.SizeOf(tcpRow));
-
-
-
-
-
-
Marshal.FreeHGlobal(buffTable);
-
-
return tcpConnectionRows;
-
-
-
-
-
public enum TCP_TABLE_TYPE : int
-
-
TCP_TABLE_BASIC_LISTENER,
-
TCP_TABLE_BASIC_CONNECTIONS,
-
-
TCP_TABLE_OWNER_PID_LISTENER,
-
TCP_TABLE_OWNER_PID_CONNECTIONS,
-
-
TCP_TABLE_OWNER_MODULE_LISTENER,
-
TCP_TABLE_OWNER_MODULE_CONNECTIONS,
-
TCP_TABLE_OWNER_MODULE_ALL
-
-
-
public enum TCP_CONNECTION_STATE : int
-
-
-
-
-
-
-
-
-
-
-
-
-
-
Program.cs:
-
-
using System.Collections.Generic;
-
-
-
-
using System.Threading.Tasks;
-
-
namespace TcpConnectionMonitor
-
-
-
-
static void Main(string[] args)
-
-
-
-
-
static void MonitorTcpConnections()
-
-
Console.WriteLine("Proto Local Address Foreign Address State PID");
-
List<String> rows = new List<string>();
-
-
-
int windowTop = Console.WindowTop;
-
TcpConnectionTableHelper.MIB_TCPROW_OWNER_PID[] tcpProgressInfoTable = TcpConnectionTableHelper.GetAllTcpConnections();
-
int tableRowCount = tcpProgressInfoTable.Length;
-
for (int i = 0; i < tableRowCount; i++)
-
-
TcpConnectionTableHelper.MIB_TCPROW_OWNER_PID row = tcpProgressInfoTable[i];
-
string source = string.Format("{0}:{1}", TcpConnectionTableHelper.GetIpAddress(row.localAddr), row.LocalPort);
-
string dest = string.Format("{0}:{1}", TcpConnectionTableHelper.GetIpAddress(row.remoteAddr), row.RemotePort);
-
string outputRow = string.Format("{0, -7}{1, -23}{2, -23}{3, -16}{4}", "TCP", source, dest, (TCP_CONNECTION_STATE)row.state, row.owningPid);
-
-
-
Console.SetCursorPosition(0, i + 1);
-
Console.WriteLine("{0, -80}", outputRow);
-
-
-
else if (rows[i] != outputRow)
-
-
-
Console.SetCursorPosition(0, i + 1);
-
Console.WriteLine("{0, -80}", outputRow);
-
-
-
if (rows.Count > tableRowCount)
-
-
int linesToBeCleared = rows.Count - tableRowCount;
-
rows.RemoveRange(tableRowCount, linesToBeCleared);
-
for (int i = 0; i < linesToBeCleared + 1; i++)
-
-
Console.WriteLine("{0, -80}", " ");
-
-
-
Console.SetWindowPosition(0, windowTop);
-
-
-
-
-
实现的效果是每 100ms 获取一次活跃 TCP 连接的状态,也就是说每秒大概会刷新10次,为了避免由于刷新频率过快导致闪烁的问题,通过调用 Console.SetCursorPosition 来对变化的数据进行部分刷新,同时为了避免滚动条存在时,Console 指针引起滚动条自动滚动到最后一行,使用 SetWindowPosition 来固定滚动条的位置。
输出结果举例:
该文章在 2022/11/17 10:05:16 编辑过