从零开发网络协议模拟器

从零开发一个网络协议模拟器------架构、引擎、可视化全流程

学了 TCP/IP 协议栈但总觉得隔着一层?搭个模拟器吧------让数据包在你画的拓扑上逐跳跑,每一层的表都能查、每一条报错都能看原因。这篇文章讲怎么用 C# WinForms 从零实现一个能拖拽、能连线、能模拟通信的网络协议教学工具。

文章目录


一、为什么要自己写

教科书的问题不是讲错了------是太抽象了。ARP 解析是几行文字描述,路由查表是一个流程图,NAT 转换是一张示意图。学生看完知道"有这么回事",但不知道真实的数据包在那条线上经过哪些设备的哪些表

模拟器的价值不是替代教材------是给教材里的每一段抽象描述配一个可视化步骤。ARP 不只是一段话,是"ARP 请求 → 收到应答 → 写入 ARP 表"这条日志。路由不止是"最长前缀匹配"这个术语,是"查路由表 → 匹配 192.168.1.0/24 → 下一跳 eth0"这条日志。

这篇文章拆开这个模拟器的完整设计和实现------从设备模型到仿真引擎到可视化交互。


二、架构:三条线并行

整个模拟器分三条线:

复制代码
模型层 (Models)
  └── Device / Port / ArpTable / MacTable / RoutingTable / NatTable / Packet
      └── 描述"网络里有什么"

仿真引擎 (SimEngine)
  └── Simulate(src, dst) → 逐跳追踪数据包
      └── 控制"数据包怎么走"

可视化层 (MainForm)
  └── 拖拽拓扑 + 双缓冲画布 + 右键交互
      └── 展示"每一步看到了什么"

模型层不依赖引擎------设备之间的连接关系是纯数据。引擎读模型数据做决策------查表、转发、封装。可视化层只负责画------设备的颜色、线的走向、日志的展示。


三、设备模型:不是画图标,是模拟真表

一个设备不是屏幕上的一张图。它是三套表的集合:

csharp 复制代码
public class Device
{
    public string IP { get; set; }        // 自身的 IP
    public string Mask { get; set; }       // 子网掩码
    public ArpTable ArpTable { get; }      // IP → MAC
    public MacTable MacTable { get; }      // MAC → 端口(仅交换机用)
    public RoutingTable RoutingTable { get; } // 目的网络 → 下一跳(仅路由器用)
    public NatTable NatTable { get; }      // 内网IP → 外网IP(仅路由器用)
    public List<Port> Ports { get; }       // 端口列表
}

ARP 表------记录 IP 和 MAC 的映射。PC 发包前查它,没有就发 ARP 广播。

MAC 表------交换机学习到的。收到一个帧时,记录"源 MAC → 来源端口"。后续帧的目的 MAC 如果在表里,直接转发到对应端口------不泛洪。

路由表 ------路由器的最核心。不是"下一个设备是谁",是"这个目的网段应该走哪个端口出去、下一跳 IP 是什么"。最长前缀匹配------192.168.1.0/24192.168.0.0/16 优先。

NAT 表------路由器做地址转换的记录。私网 IP 出去时替换为公网 IP,记录映射关系,回来的包能逆向还原。


四、端口和连线:不是画条线就完了

端口有物理位置------设备左右两侧各一排锚点。两个端口之间的连线存储为双向引用:

csharp 复制代码
portA.ConnectedTo = portB;
portB.ConnectedTo = portA;

这样仿真引擎可以从任意一个端口出发,沿着连线找到下一跳设备------不需要预先知道网络拓扑。数据包从 PC-A 出发,沿着 port → ConnectedTo → Device → 查表 → 出端口 → ConnectedTo → ... 一直走到目标。


五、仿真引擎:逐跳决策

SimEngine 的核心是一条 while 循环。每一轮代表数据包经过一个设备:

csharp 复制代码
Device currentDevice = src;
while (currentDevice != dst)
{
    if (currentDevice is Switch)
    {
        currentDevice.MacTable.Learn(源MAC, 来源端口);
        var outPort = currentDevice.MacTable.GetPort(目的MAC);
        if (outPort != null)
            currentDevice = outPort.ConnectedTo.Device;  // 已知端口 → 转发
        else
            泛洪();  // 未知 → 所有端口广播
    }
    else if (currentDevice is Router)
    {
        var route = currentDevice.RoutingTable.FindRoute(目的IP);
        if (route == null) 报错("无到达该网络的路由");
        if (route.NextHop == "0.0.0.0")
            ARP(目的IP);  // 直连网段,直接解析目标
        else
            ARP(route.NextHop);  // 非直连,解析下一跳

        重新封装帧(新源MAC=路由器出口MAC, 新目的MAC=下一跳MAC);
        if (NAT启用 && 私网→公网) SNAT转换;
        currentDevice = 出端口所连设备;
    }
}

每一步决策都生成一条日志------交换机泛洪还是转发、路由器匹配了哪条路由、NAT 转了没有。每条日志带设备名和时间,右边面板实时刷新。


六、数据包:分层可查看

每个设备只能看到自己这一层的包头内容:

  • 交换机------看到的是 L2 以太网帧:源 MAC、目的 MAC、帧类型

  • 路由器------看到的是 L3 IP 数据报:版本、TTL、源目的 IP,封装在哪个帧里

  • PC------看到完整四层:HTTP 请求 → TCP 端口 SEQ → IP 地址 → MAC 地址

    ╔══════════ IP Datagram ═══════════
    ║ 版本: IPv4
    ║ TTL: 63
    ║ 协议: TCP
    ║ 源IP: 192.168.1.10
    ║ 目的IP: 10.0.0.20
    ╠══════════ 封装 ═══════════════
    ║ [MAC:abc123] → [MAC:def456]
    ╚════════════════════════════════

这就是分层教学的核心------不是告诉你"交换机工作在第2层",是让你亲眼看见交换机只能看到帧头、看不到 IP。


七、可视化:双缓冲画布

画布是一个继承 Panel 的控件,OnPaint 里用 Graphics 画设备矩形、端口锚点、连线。关键设计:

  • 双缓冲 ------DoubleBuffered = true + AllPaintingInWmPaint,拖拽和重绘不闪烁
  • 懒渲染------OnPaint 只画当前视口附近的页面,不是整个文档,大型 PDF 也流畅
  • 设备拖拽------MouseDown 记录偏移,MouseMove 更新位置,MouseUp 释放

八、动态路由

不是只支持静态路由。点"路由交换"按钮,模拟 RIP 协议------每台路由器把自己的直连网段广播给邻居路由器,邻居学到后加入自己的路由表。日志显示:

复制代码
RIP R2 学习: 192.168.1.0/24 via 192.168.3.1 cost=1
OSPF[拥塞] R3 学习: 192.168.1.0/24 via 192.168.3.1 cost=1000

可以设置路由算法(RIP/OSPF)和状态(正常/拥塞),拥塞时 cost 乘以 10,动态路由交换会自动避开。


九、教学价值

这个模拟器的核心价值不在于技术实现------在于作为教学工具的真实性

教学概念 怎么演示
ARP 解析 PC 发起通信 → 日志"ARP 请求:谁有 192.168.1.1?"→ 收到应答
交换机 MAC 学习 第一个帧经过 → 日志"记录 MAC:xxx → 端口 P1",后续不泛洪
路由最长前缀匹配 路由器收到包 → 日志"匹配 192.168.1.0/24 → eth0"
NAT 私→公转换 PC 访问公网网站 → 日志"SNAT: 192.168.1.10 → 203.0.113.1"
DNS 跨网段查询 PC-B 跨网段查 DNS → "经网关 10.0.0.1 转至 DNS(192.168.1.2)"
故障排查 删网关、删路由、拔线 → 日志直接说"无路由"、"未连线"、"ARP 超时"

和教材的关系 :不是替代------是每学完一章,学生打开模拟器、拖拓扑、点模拟、看日志、验证刚才学的协议是不是真的这样工作


十、总结

整个模拟器约 1200 行 C#,.NET Framework 4.8,零外部依赖。三个核心:设备模型(表)、仿真引擎(逐跳决策)、可视化(画布 + 日志)。不做实际数据转发,不碰真实网络------只模拟数据包在各设备之间的表和端口之间如何流转

Github: yuhou25/net-sim


✅ 亮点:从设备表模型→仿真引擎逐跳决策→分层数据包查看→可视化交互,完整拆解了一个网络协议模拟器的设计和实现。适合做教学工具开发、网络协议教学、C# WinForms 自定义控件开发三个方向。扩展方向:加入 DHCP 自动分配 IP、ICMP 探测、BGP 多 AS 路由交换。