核心思想是调用 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 编辑过