编辑
2025-10-11
C#
00

目录

🎯 项目概览:我们要做什么?
💡 核心技术解析
🔥 技术亮点1:异步多线程架构
⚙️ 技术亮点2:高效的TCP连接检测
🛡️ 技术亮点3:智能服务识别
🎨 技术亮点4:优雅的取消机制
完整代码
📊 性能优化技巧
🚀 优化技巧1:合理设置线程数
⏱️ 优化技巧2:智能超时设置
🔧 常见问题与解决方案
❗ 问题1:内存泄漏
❗ 问题2:UI假死
❗ 问题3:扫描速度慢
💾 完整项目结构
🎯 实际应用场景
📈 扩展功能建议
🏆 总结

作为一名C#开发者,你是否曾经在排查网络问题时手忙脚乱?服务器端口是否开放、网络连接是否正常、防火墙配置是否生效... 这些问题让多少程序员深夜难眠。

今天,通过一个完整的端口扫描器项目,带你掌握C#网络编程的核心技术,从此告别网络问题排查的痛苦!

🎯 项目概览:我们要做什么?

我们要开发一个功能完整的Windows Forms端口扫描器,它具备以下核心功能:

  • 多线程并发扫描:高效处理大量端口检测
  • 实时进度显示:用户体验友好
  • 智能服务识别:自动识别常见网络服务
  • 结果导出功能:支持多种格式保存
  • 优雅的取消机制:随时中断扫描任务

💡 核心技术解析

🔥 技术亮点1:异步多线程架构

C#
private async Task ScanPortsAsync(string target, int startPort, int endPort, int threadCount, int timeout, CancellationToken cancellationToken) { var semaphore = new SemaphoreSlim(threadCount, threadCount); var tasks = new List<Task>(); for (int port = startPort; port <= endPort; port++) { if (cancellationToken.IsCancellationRequested) break; int currentPort = port; var task = Task.Run(async () => { await semaphore.WaitAsync(cancellationToken); try { await ScanPortAsync(target, currentPort, timeout, cancellationToken); } finally { semaphore.Release(); } }, cancellationToken); tasks.Add(task); } await Task.WhenAll(tasks); }

⚡ 性能优化要点:

  • SemaphoreSlim控制并发数,防止创建过多连接
  • 闭包变量捕获避免循环变量问题
  • finally块确保资源正确释放

⚙️ 技术亮点2:高效的TCP连接检测

C#
private async Task ScanPortAsync(string target, int port, int timeout, CancellationToken cancellationToken) { try { using (var client = new TcpClient()) { cancellationToken.ThrowIfCancellationRequested(); var connectTask = client.ConnectAsync(target, port); var timeoutTask = Task.Delay(timeout, cancellationToken); var completedTask = await Task.WhenAny(connectTask, timeoutTask); if (completedTask == connectTask && client.Connected) { string service = GetServiceName(port); var result = new ScanResult { Host = target, Port = port, IsOpen = true, Service = service, ScanTime = DateTime.Now }; lock (lockObject) { scanResults.Add(result); openPorts++; } Invoke(new Action(() => { AddResult($"端口 {port} 开放 - {service}", Color.Green); })); } } } catch (OperationCanceledException) { throw; } catch { } finally { lock (lockObject) { scannedPorts++; } Invoke(new Action(() => { progressBar.Value = scannedPorts; lblStatus.Text = $"正在扫描... ({scannedPorts}/{totalPorts}) 已发现 {openPorts} 个开放端口"; })); } }

🎯 关键技术点:

  • Task.WhenAny实现超时控制
  • using语句确保TCP连接释放
  • lock保证多线程数据安全
  • Invoke实现跨线程UI更新

🛡️ 技术亮点3:智能服务识别

C#
private string GetServiceName(int port) { var commonPorts = new Dictionary<int, string> { {21, "FTP"}, // 文件传输协议 {22, "SSH"}, // 安全外壳协议 {23, "Telnet"}, // 远程登录 {25, "SMTP"}, // 邮件发送 {53, "DNS"}, // 域名解析 {80, "HTTP"}, // 网页服务 {110, "POP3"}, // 邮件接收 {135, "RPC"}, // 远程过程调用 {139, "NetBIOS"}, // 网络基本输入输出系统 {143, "IMAP"}, // 邮件接收协议 {443, "HTTPS"}, // 安全网页服务 {445, "SMB"}, // 服务器消息块 {993, "IMAPS"}, // 安全IMAP {995, "POP3S"}, // 安全POP3 {1433, "MSSQL"}, // SQL Server数据库 {1521, "Oracle"}, // Oracle数据库 {3306, "MySQL"}, // MySQL数据库 {3389, "RDP"}, // 远程桌面 {5432, "PostgreSQL"}, // PostgreSQL数据库 {5900, "VNC"}, // 虚拟网络计算 {6379, "Redis"}, // Redis缓存数据库 {8080, "HTTP-Alt"}, // 备用HTTP端口 {8443, "HTTPS-Alt"} // 备用HTTPS端口 }; return commonPorts.ContainsKey(port) ? commonPorts[port] : "Unknown"; }

🎨 技术亮点4:优雅的取消机制

C#
public partial class Form1 : Form { private CancellationTokenSource cancellationTokenSource; private async void StartScan() { try { cancellationTokenSource = new CancellationTokenSource(); await ScanPortsAsync(target, startPort, endPort, threadCount, timeout, cancellationTokenSource.Token); } catch (OperationCanceledException) { lblStatus.Text = "扫描已取消"; AddResult("=== 扫描已取消 ===", Color.Red); } } private void StopScan() { cancellationTokenSource?.Cancel(); // 触发取消 } private void Form1_FormClosing(object sender, FormClosingEventArgs e) { cancellationTokenSource?.Cancel(); // 窗体关闭时取消所有任务 } }

完整代码

C#
using System; using System.Collections.Generic; using System.ComponentModel; using System.Drawing; using System.IO; using System.Net; using System.Net.NetworkInformation; using System.Net.Sockets; using System.Text; using System.Threading; using System.Threading.Tasks; using System.Windows.Forms; namespace AppPortScanner { public partial class Form1 : Form { private CancellationTokenSource cancellationTokenSource; private readonly object lockObject = new object(); private int totalPorts; private int scannedPorts; private int openPorts; private readonly List<ScanResult> scanResults = new List<ScanResult>(); public Form1() { InitializeComponent(); InitializeForm(); } private void InitializeForm() { this.Text = "端口扫描工具 v1.0"; UpdateUI(false); lblStatus.Text = "就绪"; } private void btnScan_Click(object sender, EventArgs e) { if (!ValidateInput()) return; StartScan(); } private void btnStop_Click(object sender, EventArgs e) { StopScan(); } private void btnClear_Click(object sender, EventArgs e) { lstResults.Items.Clear(); scanResults.Clear(); progressBar.Value = 0; lblStatus.Text = "就绪"; } private void btnExport_Click(object sender, EventArgs e) { ExportResults(); } private bool ValidateInput() { // 验证主机地址 string target = txtTarget.Text.Trim(); if (string.IsNullOrEmpty(target)) { MessageBox.Show("请输入主机地址!", "输入错误", MessageBoxButtons.OK, MessageBoxIcon.Warning); txtTarget.Focus(); return false; } // 验证端口范围 if (numStartPort.Value > numEndPort.Value) { MessageBox.Show("起始端口不能大于结束端口!", "输入错误", MessageBoxButtons.OK, MessageBoxIcon.Warning); numStartPort.Focus(); return false; } return true; } private async void StartScan() { try { cancellationTokenSource = new CancellationTokenSource(); UpdateUI(true); string target = txtTarget.Text.Trim(); int startPort = (int)numStartPort.Value; int endPort = (int)numEndPort.Value; int threadCount = (int)numThreads.Value; int timeout = (int)numTimeout.Value; totalPorts = endPort - startPort + 1; scannedPorts = 0; openPorts = 0; progressBar.Maximum = totalPorts; progressBar.Value = 0; lblStatus.Text = $"正在扫描 {target}:{startPort}-{endPort}..."; // 首先进行ping测试 bool isHostAlive = await PingHost(target); if (!isHostAlive) { AddResult($"警告: 主机 {target} 可能无法访问或不响应ping请求", Color.Orange); } // 开始端口扫描 await ScanPortsAsync(target, startPort, endPort, threadCount, timeout, cancellationTokenSource.Token); if (!cancellationTokenSource.Token.IsCancellationRequested) { lblStatus.Text = $"扫描完成! 共扫描 {totalPorts} 个端口,发现 {openPorts} 个开放端口"; AddResult("=== 扫描完成 ===", Color.Green); } } catch (OperationCanceledException) { lblStatus.Text = "扫描已取消"; AddResult("=== 扫描已取消 ===", Color.Red); } catch (Exception ex) { lblStatus.Text = "扫描出错"; AddResult($"错误: {ex.Message}", Color.Red); } finally { UpdateUI(false); } } private void StopScan() { cancellationTokenSource?.Cancel(); } private async Task<bool> PingHost(string target) { try { using (var ping = new Ping()) { var reply = await ping.SendPingAsync(target, 3000); return reply.Status == IPStatus.Success; } } catch { return false; } } private async Task ScanPortsAsync(string target, int startPort, int endPort, int threadCount, int timeout, CancellationToken cancellationToken) { var semaphore = new SemaphoreSlim(threadCount, threadCount); var tasks = new List<Task>(); for (int port = startPort; port <= endPort; port++) { if (cancellationToken.IsCancellationRequested) break; int currentPort = port; var task = Task.Run(async () => { await semaphore.WaitAsync(cancellationToken); try { await ScanPortAsync(target, currentPort, timeout, cancellationToken); } finally { semaphore.Release(); } }, cancellationToken); tasks.Add(task); } await Task.WhenAll(tasks); } private async Task ScanPortAsync(string target, int port, int timeout, CancellationToken cancellationToken) { try { using (var client = new TcpClient()) { cancellationToken.ThrowIfCancellationRequested(); var connectTask = client.ConnectAsync(target, port); var timeoutTask = Task.Delay(timeout, cancellationToken); var completedTask = await Task.WhenAny(connectTask, timeoutTask); if (completedTask == connectTask && client.Connected) { string service = GetServiceName(port); var result = new ScanResult { Host = target, Port = port, IsOpen = true, Service = service, ScanTime = DateTime.Now }; lock (lockObject) { scanResults.Add(result); openPorts++; } Invoke(new Action(() => { AddResult($"端口 {port} 开放 - {service}", Color.Green); })); } } } catch (OperationCanceledException) { throw; } catch { // 端口关闭或连接失败 } finally { lock (lockObject) { scannedPorts++; } Invoke(new Action(() => { progressBar.Value = scannedPorts; lblStatus.Text = $"正在扫描... ({scannedPorts}/{totalPorts}) 已发现 {openPorts} 个开放端口"; })); } } private string GetServiceName(int port) { var commonPorts = new Dictionary<int, string> { {21, "FTP"}, {22, "SSH"}, {23, "Telnet"}, {25, "SMTP"}, {53, "DNS"}, {80, "HTTP"}, {110, "POP3"}, {135, "RPC"}, {139, "NetBIOS"}, {143, "IMAP"}, {443, "HTTPS"}, {445, "SMB"}, {993, "IMAPS"}, {995, "POP3S"}, {1433, "MSSQL"}, {1521, "Oracle"}, {3306, "MySQL"}, {3389, "RDP"}, {5432, "PostgreSQL"}, {5900, "VNC"}, {6379, "Redis"}, {8080, "HTTP-Alt"}, {8443, "HTTPS-Alt"} }; return commonPorts.ContainsKey(port) ? commonPorts[port] : "Unknown"; } private void AddResult(string message, Color color) { if (InvokeRequired) { Invoke(new Action(() => AddResult(message, color))); return; } lstResults.Items.Add($"[{DateTime.Now:HH:mm:ss}] {message}"); // 滚动到最后一项 if (lstResults.Items.Count > 0) { lstResults.TopIndex = lstResults.Items.Count - 1; } } private void UpdateUI(bool isScanning) { btnScan.Enabled = !isScanning; btnStop.Enabled = isScanning; txtTarget.Enabled = !isScanning; numStartPort.Enabled = !isScanning; numEndPort.Enabled = !isScanning; numThreads.Enabled = !isScanning; numTimeout.Enabled = !isScanning; } private void ExportResults() { if (scanResults.Count == 0) { MessageBox.Show("没有扫描结果可导出!", "提示", MessageBoxButtons.OK, MessageBoxIcon.Information); return; } using (var saveDialog = new SaveFileDialog()) { saveDialog.Filter = "文本文件 (*.txt)|*.txt|CSV文件 (*.csv)|*.csv"; saveDialog.FileName = $"PortScan_{DateTime.Now:yyyyMMdd_HHmmss}"; if (saveDialog.ShowDialog() == DialogResult.OK) { try { var content = new StringBuilder(); if (saveDialog.FilterIndex == 1) // TXT格式 { content.AppendLine("端口扫描结果"); content.AppendLine("================"); content.AppendLine($"扫描时间: {DateTime.Now:yyyy-MM-dd HH:mm:ss}"); content.AppendLine($"目标主机: {txtTarget.Text}"); content.AppendLine($"端口范围: {numStartPort.Value}-{numEndPort.Value}"); content.AppendLine($"开放端口数: {openPorts}"); content.AppendLine(); foreach (var result in scanResults) { if (result.IsOpen) { content.AppendLine($"{result.Host}:{result.Port} - {result.Service}"); } } } else // CSV格式 { content.AppendLine("主机,端口,状态,服务,扫描时间"); foreach (var result in scanResults) { if (result.IsOpen) { content.AppendLine($"{result.Host},{result.Port},开放,{result.Service},{result.ScanTime:yyyy-MM-dd HH:mm:ss}"); } } } File.WriteAllText(saveDialog.FileName, content.ToString(), Encoding.UTF8); MessageBox.Show($"结果已导出到: {saveDialog.FileName}", "导出成功", MessageBoxButtons.OK, MessageBoxIcon.Information); } catch (Exception ex) { MessageBox.Show($"导出失败: {ex.Message}", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error); } } } } private void Form1_FormClosing(object sender, FormClosingEventArgs e) { cancellationTokenSource?.Cancel(); } } public class ScanResult { public string Host { get; set; } public int Port { get; set; } public bool IsOpen { get; set; } public string Service { get; set; } public DateTime ScanTime { get; set; } } }

image.png

📊 性能优化技巧

🚀 优化技巧1:合理设置线程数

C#
// 根据CPU核心数动态调整默认线程数 int defaultThreadCount = Environment.ProcessorCount * 4; numThreads.Value = Math.Min(defaultThreadCount, 100); // 最大不超过100

⏱️ 优化技巧2:智能超时设置

C#
// 根据网络环境动态调整超时时间 private async Task<int> GetOptimalTimeout(string target) { try { using (var ping = new Ping()) { var reply = await ping.SendPingAsync(target, 1000); if (reply.Status == IPStatus.Success) { // 根据ping延迟调整超时时间 return Math.Max((int)reply.RoundtripTime * 3, 1000); } } } catch { } return 3000; // 默认3秒超时 }

🔧 常见问题与解决方案

❗ 问题1:内存泄漏

解决方案: 正确使用using语句和Dispose模式

❗ 问题2:UI假死

解决方案: 使用async/awaitInvoke正确处理跨线程操作

❗ 问题3:扫描速度慢

解决方案: 合理设置并发数和超时时间,避免网络拥塞

💾 完整项目结构

C#
AppPortScanner/ ├── Form1.cs // 主窗体逻辑 ├── Form1.Designer.cs // 窗体设计器代码 ├── ScanResult.cs // 扫描结果数据模型 ├── Program.cs // 程序入口点 └── App.config // 应用配置文件

🎯 实际应用场景

  1. 网络管理员:快速检测服务器端口状态
  2. 开发人员:验证应用程序网络配置
  3. 安全测试:发现潜在的安全风险点
  4. 运维工程师:自动化网络健康检查

📈 扩展功能建议

  • 添加端口指纹识别功能
  • 集成漏洞检测模块
  • 支持IPv6协议扫描
  • 实现分布式扫描架构
  • 添加实时网络拓扑图

🏆 总结

通过这个端口扫描器项目,我们掌握了C#网络编程的三大核心技术:

  1. 异步编程模式:提高应用程序响应性和性能
  2. 多线程并发控制:合理利用系统资源
  3. 网络通信协议:深入理解TCP/IP协议栈

这些技术不仅适用于端口扫描,更是现代C#开发中不可或缺的技能。掌握了这些,你就能轻松处理各种网络编程挑战!


💬 互动时间:

  1. 你在实际项目中遇到过哪些网络编程难题?
  2. 对于大规模端口扫描,你还有什么优化建议?

觉得有用的话,记得分享给更多的C#同行!让我们一起在技术路上越走越远! 🚀

相关信息

通过网盘分享的文件:AppPortScan.zip 链接: https://pan.baidu.com/s/1ky5gjulEVLMoGUQRMH83Zw?pwd=nnbc 提取码: nnbc --来自百度网盘超级会员v9的分享

本文作者:技术老小子

本文链接:

版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!