|
|
#region << 版 本 注 释 >>
|
|
|
|
|
|
/*--------------------------------------------------------------------
|
|
|
* 版权所有 (c) 2026 WenJY 保留所有权利。
|
|
|
* CLR版本:4.0.30319.42000
|
|
|
* 机器名称:Mr.Wen's MacBook Pro
|
|
|
* 命名空间:Sln.IntelliBelt.Socket
|
|
|
* 唯一标识:E4DF9972-3038-4900-A2C1-507F30B63F54
|
|
|
*
|
|
|
* 创建者:WenJY
|
|
|
* 电子邮箱:
|
|
|
* 创建时间:2026-05-27 11:13:35
|
|
|
* 版本:V1.0.0
|
|
|
* 描述:
|
|
|
*
|
|
|
*--------------------------------------------------------------------
|
|
|
* 修改人:
|
|
|
* 时间:
|
|
|
* 修改说明:
|
|
|
*
|
|
|
* 版本:V1.0.0
|
|
|
*--------------------------------------------------------------------*/
|
|
|
|
|
|
#endregion << 版 本 注 释 >>
|
|
|
|
|
|
using System.Diagnostics;
|
|
|
using System.Runtime.InteropServices;
|
|
|
|
|
|
namespace Sln.IntelliBelt.Socket;
|
|
|
|
|
|
/// <summary>
|
|
|
/// 端口信息查询结果
|
|
|
/// </summary>
|
|
|
public class PortProcessInfo
|
|
|
{
|
|
|
public int Port { get; set; }
|
|
|
public string ProcessName { get; set; }
|
|
|
public int Pid { get; set; }
|
|
|
public string LocalAddress { get; set; }
|
|
|
public string RemoteAddress { get; set; }
|
|
|
public string State { get; set; }
|
|
|
|
|
|
public override string ToString()
|
|
|
{
|
|
|
return $"端口:{Port} | 程序:{ProcessName} | PID:{Pid} | {LocalAddress}->{RemoteAddress} | 状态:{State}";
|
|
|
}
|
|
|
}
|
|
|
|
|
|
public static class PortHelper
|
|
|
{
|
|
|
/// <summary>
|
|
|
/// 根据端口号获取使用该端口的程序信息
|
|
|
/// </summary>
|
|
|
/// <param name="port">端口号</param>
|
|
|
/// <returns>程序信息列表</returns>
|
|
|
public static List<PortProcessInfo> GetProcessByPort(int port)
|
|
|
{
|
|
|
if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)
|
|
|
|| RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
|
|
|
{
|
|
|
return GetProcessByPortUnix(port);
|
|
|
}
|
|
|
else if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
|
|
|
{
|
|
|
return GetProcessByPortWindows(port);
|
|
|
}
|
|
|
|
|
|
throw new PlatformNotSupportedException("不支持当前操作系统");
|
|
|
}
|
|
|
|
|
|
#region Unix (macOS / Linux)
|
|
|
|
|
|
private static List<PortProcessInfo> GetProcessByPortUnix(int port)
|
|
|
{
|
|
|
var result = new List<PortProcessInfo>();
|
|
|
|
|
|
try
|
|
|
{
|
|
|
var psi = new ProcessStartInfo
|
|
|
{
|
|
|
FileName = "lsof",
|
|
|
Arguments = $"-i :{port} -n -P",
|
|
|
RedirectStandardOutput = true,
|
|
|
RedirectStandardError = true,
|
|
|
UseShellExecute = false,
|
|
|
CreateNoWindow = true
|
|
|
};
|
|
|
|
|
|
using var process = Process.Start(psi);
|
|
|
var output = process!.StandardOutput.ReadToEnd();
|
|
|
process.WaitForExit();
|
|
|
|
|
|
var lines = output.Split('\n', StringSplitOptions.RemoveEmptyEntries);
|
|
|
// 跳过表头(第一行)
|
|
|
foreach (var line in lines.Skip(1))
|
|
|
{
|
|
|
var parts = line.Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
|
|
|
if (parts.Length < 9) continue;
|
|
|
|
|
|
var command = parts[0];
|
|
|
var pidStr = parts[1];
|
|
|
var namePart = parts[8]; // 格式: localhost:port->localhost:port 或 *:port
|
|
|
|
|
|
if (!int.TryParse(pidStr, out int pid)) continue;
|
|
|
|
|
|
var info = new PortProcessInfo
|
|
|
{
|
|
|
Port = port,
|
|
|
ProcessName = command,
|
|
|
Pid = pid
|
|
|
};
|
|
|
|
|
|
// 解析地址和状态
|
|
|
ParseUnixConnection(namePart, info);
|
|
|
|
|
|
// 如果有状态标识(如 (ESTABLISHED)),取最后一个括号内的内容
|
|
|
var lastPart = parts[^1];
|
|
|
if (lastPart.StartsWith('(') && lastPart.EndsWith(')'))
|
|
|
{
|
|
|
info.State = lastPart.Trim('(', ')');
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
info.State = "LISTEN";
|
|
|
}
|
|
|
|
|
|
result.Add(info);
|
|
|
}
|
|
|
}
|
|
|
catch (Exception ex)
|
|
|
{
|
|
|
Console.Error.WriteLine($"执行 lsof 失败: {ex.Message}");
|
|
|
}
|
|
|
|
|
|
return result;
|
|
|
}
|
|
|
|
|
|
private static void ParseUnixConnection(string namePart, PortProcessInfo info)
|
|
|
{
|
|
|
// 格式类似: localhost:6000->localhost:51183 或 *:51183
|
|
|
var arrowIdx = namePart.IndexOf("->");
|
|
|
if (arrowIdx > 0)
|
|
|
{
|
|
|
info.LocalAddress = namePart[..arrowIdx];
|
|
|
info.RemoteAddress = namePart[(arrowIdx + 2)..];
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
info.LocalAddress = namePart;
|
|
|
info.RemoteAddress = "-";
|
|
|
}
|
|
|
}
|
|
|
|
|
|
#endregion
|
|
|
|
|
|
#region Windows
|
|
|
|
|
|
private static List<PortProcessInfo> GetProcessByPortWindows(int port)
|
|
|
{
|
|
|
var result = new List<PortProcessInfo>();
|
|
|
|
|
|
try
|
|
|
{
|
|
|
// 1. 用 netstat 找到 PID
|
|
|
var connections = new List<(string local, string remote, string state, int pid)>();
|
|
|
|
|
|
var psi = new ProcessStartInfo
|
|
|
{
|
|
|
FileName = "netstat",
|
|
|
Arguments = "-ano",
|
|
|
RedirectStandardOutput = true,
|
|
|
RedirectStandardError = true,
|
|
|
UseShellExecute = false,
|
|
|
CreateNoWindow = true
|
|
|
};
|
|
|
|
|
|
using var process = Process.Start(psi);
|
|
|
var output = process!.StandardOutput.ReadToEnd();
|
|
|
process.WaitForExit();
|
|
|
|
|
|
foreach (var line in output.Split('\n', StringSplitOptions.RemoveEmptyEntries).Skip(4))
|
|
|
{
|
|
|
var parts = line.Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
|
|
|
if (parts.Length < 5) continue;
|
|
|
|
|
|
var localAddr = parts[1]; // 如 0.0.0.0:8080
|
|
|
var remoteAddr = parts[2];
|
|
|
var state = parts.Length == 5 ? "LISTEN" : parts[3];
|
|
|
|
|
|
// 取最后一列作为 PID
|
|
|
var lastPart = parts[^1];
|
|
|
if (!int.TryParse(lastPart, out int pid)) continue;
|
|
|
|
|
|
// 检查是否匹配目标端口
|
|
|
var localPort = localAddr.Split(':').Last();
|
|
|
var remotePort = remoteAddr.Split(':').Last();
|
|
|
if (localPort == port.ToString() || remotePort == port.ToString())
|
|
|
{
|
|
|
connections.Add((localAddr, remoteAddr, state, pid));
|
|
|
}
|
|
|
}
|
|
|
|
|
|
// 2. 用 tasklist 找程序名
|
|
|
var pidNameMap = new Dictionary<int, string>();
|
|
|
psi = new ProcessStartInfo
|
|
|
{
|
|
|
FileName = "tasklist",
|
|
|
Arguments = "/FO CSV /NH",
|
|
|
RedirectStandardOutput = true,
|
|
|
RedirectStandardError = true,
|
|
|
UseShellExecute = false,
|
|
|
CreateNoWindow = true
|
|
|
};
|
|
|
|
|
|
using var process2 = Process.Start(psi);
|
|
|
var tasklistOutput = process2!.StandardOutput.ReadToEnd();
|
|
|
process2.WaitForExit();
|
|
|
|
|
|
foreach (var line in tasklistOutput.Split('\n', StringSplitOptions.RemoveEmptyEntries))
|
|
|
{
|
|
|
var trimmed = line.Trim().Trim('"');
|
|
|
var csvParts = trimmed.Split("\",\"");
|
|
|
if (csvParts.Length >= 2 && int.TryParse(csvParts[1], out int pid))
|
|
|
{
|
|
|
pidNameMap[pid] = csvParts[0];
|
|
|
}
|
|
|
}
|
|
|
|
|
|
// 3. 组装结果
|
|
|
foreach (var (local, remote, state, pid) in connections)
|
|
|
{
|
|
|
var processName = pidNameMap.TryGetValue(pid, out var name) ? name : "(未知)";
|
|
|
result.Add(new PortProcessInfo
|
|
|
{
|
|
|
Port = port,
|
|
|
ProcessName = processName,
|
|
|
Pid = pid,
|
|
|
LocalAddress = local,
|
|
|
RemoteAddress = remote,
|
|
|
State = state
|
|
|
});
|
|
|
}
|
|
|
}
|
|
|
catch (Exception ex)
|
|
|
{
|
|
|
Console.Error.WriteLine($"查询失败: {ex.Message}");
|
|
|
}
|
|
|
|
|
|
return result;
|
|
|
}
|
|
|
|
|
|
#endregion
|
|
|
|
|
|
/// <summary>
|
|
|
/// 判断端口是否被本地 Intel 程序(Sln.Intel)占用
|
|
|
/// </summary>
|
|
|
/// <param name="port">端口号</param>
|
|
|
/// <returns>true 表示端口被本地 Sln.Intel 相关进程占用</returns>
|
|
|
public static bool IsIntelLocalPort(int port)
|
|
|
{
|
|
|
var list = GetProcessByPort(port);
|
|
|
|
|
|
return list.Any(info =>
|
|
|
// 程序名包含 Sln.Intel(忽略大小写)
|
|
|
info.ProcessName.Contains("Sln.Intel",
|
|
|
StringComparison.OrdinalIgnoreCase) //&& IsLocalAddress(info.RemoteAddress);;
|
|
|
// 且连接双方都在本机(localhost 或 127.0.0.1)
|
|
|
&& IsLocalAddress(info.LocalAddress)
|
|
|
&& IsLocalAddress(info.RemoteAddress));
|
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
|
/// 判断地址是否为本机地址
|
|
|
/// </summary>
|
|
|
private static bool IsLocalAddress(string address)
|
|
|
{
|
|
|
if (string.IsNullOrEmpty(address)) return false;
|
|
|
|
|
|
// 支持 localhost、127.0.0.1、*:* 等本机形式
|
|
|
return address.StartsWith("localhost:", StringComparison.OrdinalIgnoreCase)
|
|
|
|| address.StartsWith("127.0.0.1:", StringComparison.OrdinalIgnoreCase)
|
|
|
|| address.StartsWith("*:", StringComparison.OrdinalIgnoreCase)
|
|
|
|| address.StartsWith("[::1]:", StringComparison.OrdinalIgnoreCase)
|
|
|
|| address.StartsWith("0.0.0.0:", StringComparison.OrdinalIgnoreCase);
|
|
|
}
|
|
|
|
|
|
#region 便捷方法
|
|
|
|
|
|
/// <summary>
|
|
|
/// 获取端口对应的第一个程序名称(最常用)
|
|
|
/// </summary>
|
|
|
/// <param name="port">端口号</param>
|
|
|
/// <returns>程序名,无结果时返回 null</returns>
|
|
|
public static string? GetProcessNameByPort(int port)
|
|
|
{
|
|
|
var list = GetProcessByPort(port);
|
|
|
return list.FirstOrDefault()?.ProcessName;
|
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
|
/// 输出端口占用详情到控制台
|
|
|
/// </summary>
|
|
|
public static void PrintPortInfo(int port)
|
|
|
{
|
|
|
var list = GetProcessByPort(port);
|
|
|
if (list.Count == 0)
|
|
|
{
|
|
|
Console.WriteLine($"端口 {port} 没有被任何程序占用");
|
|
|
return;
|
|
|
}
|
|
|
foreach (var item in list)
|
|
|
{
|
|
|
Console.WriteLine(item);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
#endregion
|
|
|
} |