☰
POX控制器学习笔记:从OpenFlow到自学习交换机的实践指南
2026/10/14 8:52:59 网站建设 项目流程

1. 初识POX:这个控制器为什么值得写学习笔记

1.1 POX是什么,它和OpenFlow/SDN是什么关系

先聊一个基础但很多人一上来就容易糊掉的问题:POX到底是个什么东西。

POX是一个基于Python实现的开源OpenFlow控制器,是早期NOX控制器的Python版本演变而来的。它在SDN体系里负责“控制平面”这一层:底层网络设备(大多是支持OpenFlow的交换机或软件交换机)把数据包处理的决定权上交给控制器,由控制器统一计算转发逻辑,再以流表项的形式下发给交换机执行。简单说,交换机负责“跑腿”,POX负责“动脑”。

你在做SDN实验或学习的时候,通常会碰到三个联动的东西:Mininet负责在一台电脑上模拟出交换机、主机和链路;OpenFlow协议负责连接控制器和交换机;POX控制器则提供了一套Python接口,让你用写脚本的方式定义网络行为。比如你可以写一段代码,让控制器收到未知目的MAC的报文时把它从所有端口广播出去;或者让两个主机之间的流量走一条特定路径。这些逻辑写起来比在路由器上敲命令行更灵活,也更直观。

我最初接触POX时最大的误解是,以为它是个“网络操作系统”,能管理一切。实际上POX远没有那么重,它更像一个事件驱动的组件框架:你注册了某个组件的监听回调,当网络里有对应事件发生时,POX就把事件对象丢给你的函数,你在里面写处理逻辑就行。这种模型在学习阶段反而非常友好,因为你不需要理解复杂的多线程或异步机制,只需要盯住“事件来了,我怎么回”这一个中心。

1.2 为什么我推荐从POX入门控制器开发

现在市面上控制器可太多了,比如RYU、Floodlight、ONOS、ODL,还有后起之秀们。很多人会问:为什么还要学POX这种看起来有点老气的控制器?

我的答案是:正因为POX“老”,所以它简单、稳定、资料多。我见过太多初学者一上来就选一个企业级控制器,结果光搭环境、配集群就折腾一周,连流表长什么样都没见到。POX不需要数据库,不需要集群协调器,也不要求你用什么复杂的构建工具。你只要有一个能跑Python的环境,git clone下来直接就能启动一个完整的、可用的SDN控制平面。

另一个重要原因是,POX的代码量适中,内部结构清晰。相比之下,某些控制器的代码包了一层又一层的抽象,你很难在短时间内看清楚一条流表从下发到生效的完整路径。POX源码里,openflow.of_01模块就是OpenFlow 1.0协议的直接封装,forwarding.l2_learning就是一个完整可以运行的自学习交换机实现。你在学习群里看到的很多SDN入门实验教程,底层跑的还是POX。

当然,POX不是万能的。它的OpenFlow版本支持停留在1.0,部分组件在大型网络下性能一般,官方更新也不活跃。但它用来学习、实验、二次开发教学演示,简直是量身定做。我的建议是:先通过POX把SDN的核心概念建立起来,等理解了事件机制、流表操作、拓扑发现这一套以后再迁移到其他控制器,你会发现很多概念是相通的,那时学习速度会快很多。

1.3 POX整体架构:组件、事件、消息

我学习POX时,觉得最难的不是Python语法,而是理解它的运行模型。这里先铺垫核心框架,后面再展开具体操作。

POX的启动入口是pox.py脚本,它负责加载你指定的组件列表来启动对应服务。比如执行:

./pox.py forwarding.l2_learning

POX就会先启动核心服务,再加载forwarding.l2_learning组件。组件之间通过核心对象core交换数据,监听和发送事件。core.openflow是核心的OpenFlow事件源,所有交换机连接状态变化、PacketIn消息到达,都会从这里发出事件。

再看消息层面。OpenFlow交换机与控制器之间的通信是基于消息的,比如ofp_packet_in、ofp_flow_mod、ofp_packet_out。POX里的libopenflow_01模块,就是把OpenFlow 1.0协议的消息结构映射成了Python对象。你在代码里写of.ofp_action_output(port=3),实际上就是构造了一条OpenFlow指令,让交换机的某个端口执行输出动作。

理清这三层关系后,我建议大家做一个小实验:启动POX后连上一台Mininet交换机,然后在POX里打断点观察事件对象都有哪些属性。我自己当初就是这么干的,花了一个下午看事件属性,比看十篇教程都管用。因为后面写处理逻辑时,90%的代码都是在读事件的字段:交换机ID是多少、入端口是哪个、源MAC目标MAC是什么、缓冲区ID是多少,这些字段全部都在事件对象里面。

2. 环境搭建与第一个可运行的控制器

2.1 准备Mininet和Python环境

搭建环境这一步,很多人常年在同一条河里翻船,所以我单独拿出来记录一下。

如果你用的是新版Linux发行版,环境里大概率默认是Python 3;但POX本身是Python 2时代的项目,代码里大量使用Python 2语法,直接用Python 3跑会有兼容问题。我踩过的坑是:直接执行python pox.py,解释器提示语法错误,后来才意识到系统里只装了Python 3。我的解决办法是在系统里安装Python 2解释器,或者干脆用一个只装了Python 2的容器来跑。相比修补POX的Python 3兼容问题,直接准备一个Python 2环境要省心得多。

Mininet的安装则需要做一次选择:直接通过包管理器安装,通常只会带上一组基本拓扑命令,适合跑现成实验;如果需要更多自定义拓扑脚本或需要研究Mininet源码,则建议从源码安装。我用的是源码方式,因为这样mn命令能和我自己的SDN实验脚本配合得更顺畅。安装过程中最常见的问题是依赖模块缺失,比如setuptools版本过旧,或者cffi编译失败,遇到这类问题优先检查Python版本相关依赖,不要急着重装系统。

环境确认的最终标准是三个命令都能正常执行:

sudo mn --version python --version # 确保是Python 2.7的某个版本 git --version

如果Mininet能创建出拓扑并且响应命令,环境就算基本合格了。这时候再折腾POX,错误就会少一半。

2.2 安装并启动POX

POX不需要安装,它的使用方式是直接运行仓库里的pox.py脚本。这也是我最初比较不适应的一个地方,后来才理解这样做的理由:POX组件是松耦合的Python模块,直接从一个目录运行可以避免复杂的包安装和路径配置。

克隆仓库后,切到项目目录,执行:

./pox.py forwarding.l2_learning

如果一切正常,你会看到日志输出,最后一行通常是类似“POX 0.x.x going up...”的信息,然后进程就停在前台等待交换机连接。这一步就说明控制器已经跑起来了,默认监听6633端口。

我要特别提醒新手的第一坑:一定要在项目根目录下执行,因为POX导入组件时是基于当前工作目录的。还有一次我换了个终端,忘了用sudo,结果绑定端口失败。端口绑定需要权限,建议直接在命令前加sudo,省得错一个地方排查半天。

启动过程中如果遇到ImportError: No module named pox,那就是启动目录不对或PYTHONPATH没设置好。你应该检查当前目录是否包含pox子目录,然后重新执行。这算是POX新手最容易遇到的启动问题,没有之一。

2.3 验证控制器是否真正工作

控制器启动之后,很多人的做法是直接跑Mininet,然后发现网络不通,于是开始怀疑人生。其实更好的习惯是先做一次最小的连通性验证,把控制平面和实验拓扑分开排查。

我的验证顺序是这样:

先把Mininet里的一个简单拓扑连到本地控制器:

sudo mn --topo=minimal --controller=remote,ip=127.0.0.1,port=6633

这里的关键参数是--controller=remote,表示Mininet创建的OpenFlow交换机要连接到一个外部控制器,后面的ip和port指的就是POX监听的地址端口。

在Mininet启动过程中,你会看到终端打印创建主机和交换机的信息,而POX那边会输出一条交换机连接建立的事件日志,通常会包含交换机ID和端口信息。看到这条日志,才可以确定控制平面和数据平面已经打通。

接着在Mininet内执行:

mininet> pingall

如果你加载的是forwarding.l2_learning,minimal拓扑下h1和h2应该是可以互相ping通的。ping通不代表你理解了流程,我建议此时再执行dpctl dump-flows之类查看流表命令,看看交换机里到底下发了几条流表、匹配字段是什么、动作是什么。这一步能把“控制器下发了流表”从抽象概念变成眼前可见的事实,价值非常大。

如果你在这台机器上已经跑过其他控制器实验,就别忘了先清理之前的环境,否则很容易出现“明明连续了但流量无法转发”的诡异问题。我的惯例是执行sudo mn -c清理,然后重启POX再继续,这个习惯帮我省了非常多时间。

3. 核心组件拆解:这些内置模块是怎么工作的

3.1 forwarding.l2_learning:二层自学习交换机的实现逻辑

POX内置了许多组件,但我觉得forwarding.l2_learning是学习价值最高的一个。它的功能相当于一个二层交换机:交换机一开始不知道主机MAC地址在哪个端口,控制器收到未知目的MAC的报文时,就把它泛洪到所有端口;而收到未知源MAC的报文时,则记录源MAC与入端口的对应关系,这个对应关系就是“MAC地址表”。

打开这个组件的代码,你会发现它的核心数据结构就是一个字典:键是交换机ID加上MAC地址,值是对应的交换机端口。每次PacketIn事件到达,控制器先更新源地址的映射,再查目标地址的映射;查得到就走单端口转发,查不到就广播。查得到的情况下,它还会顺便下发一条流表项,让这条流的后续报文直接在交换机内匹配转发,不再上送控制器。

这段逻辑看起来简单,但我认为它是几乎所有高级转发策略的起点。比如你要做基于VLAN的隔离转发,要做的就是把MAC表改成“交换机ID+VLAN+MAC”作为键;要做链路负载均衡,就要在查表时增加路径选择逻辑。理解了基础版本之后,再往每个方向扩展都有迹可循。

它在实际操作中还有一个容易被忽略的细节:流表下发时的空闲超时时间。默认情况下,l2_learning会给流表项设置一个空闲超时时间,超过时间没有匹配流量,交换机就会自动删除该流表项。我在实验里就遇到过这样的现象:打开网页能通,过一阵子再去ping就ping不通了,一看流表,原来的流表项已经被清理了。这其实不是故障,而是空闲超时的正常表现。

3.2 openflow.discovery与拓扑发现

第二个值得深入研究的内置组件是openflow.discovery,它负责自动发现交换机和交换机之间的链路,是很多上层应用(比如路由算法、拓扑可视化)的基础依赖。

它的原理用一句话概括就是:控制器向每个交换机的每个端口发送LLDP报文,交换机按照OpenFlow协议把它封装成特定格式的消息送给邻居交换机,邻居交换机收到后再通过PacketIn消息发回给控制器。控制器通过这些LLDP报文内容,就能知道自己的某个交换机某个端口,连接到了另一台交换机的某个端口。这样,整个网络的物理链路拓扑就一目了然了。

LLDP本身是一种链路层发现协议,在传统网络设备里也常用,只不过在SDN场景下,由控制器统一发起和管理。POX的openflow.discovery组件会把发现到的链路数据记录在core.topology里,其他组件可以通过查询core.topology接口获取邻居关系。

我自己做实验时,不会只看日志说有链路,而是在脚本里真正调用拓扑接口打印一次链路关系。因为只有实际输出了[s1, 1, s2, 2]这样的结果,你才会确信POX的拓扑发现是真的在工作,而不仅仅是日志模块碰巧在打印信息。

3.3 其他高频组件一览

除了l2_learning和discovery,POX里还有几个组件,我建议学习时也顺手跑一遍。

  • forwarding.l2_pairs:它和l2_learning思路相似,但记录的是“源MAC+目的MAC”二元组对应的端口,而不是单纯按源MAC学习。效果上会更精确,但内存和流表条目也更多。
  • host_tracker:专门跟踪主机位于哪台交换机哪个端口,输出主机在线/离线的迁移事件。对实现主机位置感知的应用非常有用。
  • web.json:提供一个HTTP JSON接口,可以通过REST方式查询控制器的运行状态。初学时可以不用,但做可视化或自动化脚本时非常香。
  • openflow.spanning_tree:提供生成树协议,在存在环路拓扑时避免广播风暴。我建议等理解了基础转发后,再去尝试验证这个组件的效果。

学习这些组件时,不要只看名字猜测功能,我的方法是每个组件都起一个Mininet拓扑,观察那套拓扑在组件作用下产生的流表和转发行为差异。比如同一个拓扑,在l2_learning和l2_pairs下跑pingall,然后对比流表内容,你对两种算法的差异会理解得非常深刻。

4. 手写第一个POX应用:从hello到MAC学习控制器

4.1 事件监听和连接处理:ConnectionUp与PacketIn

在拆完内置组件后,真正动手写一个自己的控制器,我觉得才算开始入门SDN开发了。

POX的编程模型非常统一。先导入核心库,然后定义一个类,在类里注册事件监听函数,最后通过launch()把类注册进核心服务。最常见的事件有两个:ConnectionUp和PacketIn。前者在交换机连接建立时触发,你可以在里面记录交换机的连接对象,为后续向交换机发配置消息做准备;后者是交换机收到无法匹配流表的数据包后把它发送给控制器时触发,也就是你编写转发策略的核心入口。

我以一个自己写过的l2_learning简化版为例,展示核心代码框架:

from pox.core import core import pox.openflow.libopenflow_01 as of from pox.lib.addresses import EthAddr log = core.getLogger() class SwitchController(object): def __init__(self): core.openflow.addListeners(self) # mac_table以交换机ID为外层键,MAC地址为内层键,端口号为值 self.mac_table = {} def _handle_ConnectionUp(self, event): log.info("Switch %s connected", event.connection.dpid) self.mac_table.setdefault(event.connection.dpid, {}) def _handle_PacketIn(self, event): packet = event.parsed if not packet.parsed: log.warning("Ignoring incomplete packet") return mac_src = packet.src mac_dst = packet.dst in_port = event.port dpid = event.connection.dpid # 学习源MAC地址对应的端口 self.mac_table[dpid][mac_src] = in_port out_port = self.mac_table[dpid].get(mac_dst) actions = [] if out_port is None: # 未知目的MAC,泛洪 out_port = of.OFPP_FLOOD actions.append(of.ofp_action_output(port=out_port)) # 组装packet_out消息直接转发 msg = of.ofp_packet_out() msg.data = event.ofp msg.actions = actions event.connection.send(msg) # 如果已知目的端口,则下发流表项供后续报文命中 if out_port != of.OFPP_FLOOD: match = of.ofp_match(in_port=in_port, dl_src=mac_src, dl_dst=mac_dst) flow_mod = of.ofp_flow_mod(match=match, actions=actions) event.connection.send(flow_mod) def launch(): core.registerNew(SwitchController)

这段代码虽然只有几十行,但它已经是一个完整的、可以在POX里运行的两层自学习控制器了。我建议你在自己的环境里逐行敲一遍,不要复制粘贴,因为逐行敲过的代码你会留意到很多细节,比如event.ofp和event.parsed的区别,ofp_packet_out的数据字段应该填什么,flow_mod和packet_out各自负责什么。这些细节才是真正值钱的学习内容。

4.2 MAC地址学习与流表下发:代码背后的设计逻辑

代码里有个地方我想单独展开讲,就是为什么要同时发送packet_out和flow_mod两种消息。

packet_out是即时性的,它的作用是告诉交换机“这个包现在从某个端口发出去”;而flow_mod是规则性的,它的作用是告诉交换机“以后凡是符合匹配条件的包,都按这些动作处理”。前者让当前这个包能到达正确目的地,后者让后续同类的包不需要再上送控制器,直接在交换机本地处理。两者配合,就实现了SDN控制平面主导下的数据平面快速转发。

这也顺便回答了初学者常问的问题:为什么第一次ping总是慢,第二次就快了?因为第一个包走了控制器路径,经历了一次“控制器决策→下发流表→转发”的完整过程,后续的包在流表直接命中,所以速度快了很多。理解了这一点,你在调试网络延迟现象时就有了正确的排查方向。

关于buffer_id这里也提醒一下。OpenFlow交换机在把无法匹配的数据包上送给控制器时,可以顺便把报文内容缓存一部分,并在PacketIn消息里带上一个buffer_id。如果你的flow_mod里带了buffer_id,交换机就可以直接用缓存的数据执行动作,而不需要控制器再把报文内容放进packet_out里重复发送。我在第一个版本里没有处理这个字段,导致某些交换机会出现双倍发送的现象,后来参考了官方组件实现,把buffer_id也传进去,问题才消失。

对于学习阶段而言,性能优化可以先放一边,正确性永远是第一位的。你只要保证自己的packet_out里msg.data = event.ofp,这个字段能把原始报文带回去,转发就不会丢包。

4.3 在Mininet中验证:对照实验前后对比

代码写完之后,我把POX换成自己的控制器模块,重新在Mininet里跑了一遍验证。

启动方式很简单:

sudo python pox.py my_controller

然后在另一个终端启动Mininet:

sudo mn --topo=minimal --controller=remote,ip=127.0.0.1,port=6633

随后我用了两个步骤来验证学习效果。第一步是pingall,检查基本连通性。最小拓扑下h1和h2应该能通。第二步是逐个查看流表,我用的命令是:

mininet> dpctl dump-flows

正常执行几次ping之后,流表里应该能看到几条流表项,它们分别记录了从主机1到主机2、反向从主机2到主机1的匹配规则。再对照一下代码里下发的流表时机的代码,你会发现每一次发包触发的学习和转发过程都被完整地记录到了流表里。

我还做了另一个更有意思的对照实验:先用未知目的MAC去ping,抓包确认第一个包会被泛洪;然后又连续ping几次,观察后面是否不再出现泛洪,而只出现定向转发。这种前后对比让我对自学习算法的“自适应能力”有了直观感受,比单纯看代码理解要深入得多。

5. 学习过程中的坑:常见问题与排查思路

5.1 控制器能启动但ping不通,先查这几处

我自己在学习POX时,遇到最多的问题类型不是代码报错,而是“代码能跑,但网络就是不通”。这种问题最难排查,因为没有一个固定语法错误告诉你哪里错了。后来我总结出一套固定的排查顺序,分享在这里。

第一步,先确认交换机是否真的连上了控制器。看POX日志里有没有出现ConnectionUp事件。如果没有,检查Mininet是否设置了--controller=remote,以及ip、port参数是否和POX启动参数一致。这种“最低级但最常见”的问题,往往就是因为启动Mininet时忘了加remote参数。

第二步,确认是否有PacketIn事件。如果交换机连上了但控制器没有收到PacketIn,通常是流表没有被正确清空或交换机里还有残留的转发规则。先用dpctl dump-flows看看,如果流表很满且规则怪异,建议重启Mininet或干脆sudo mn -c清理后重新创建拓扑。

第三步,确认流表方向和匹配字段。我在自己的控制器里就遇到过因为匹配字段只写了dl_dst而没有写in_port导致的环回问题,单看逻辑没发现,实际抓包才发现数据包在交换机内部打转。把匹配字段的每条都打印出来对照,基本能定位问题。

第四步,检查网络本身是否正常。我不要总是怀疑控制器,有时候问题是Mininet自身的树拓扑没有建对,或者主机的ARP请求在泛洪时被丢弃了。这时候可以退一步,先不带控制器跑一个简单拓扑,看看传统内核交换机是否能通。如果连内核交换机都不通,那问题一定在Mininet和主机网络配置里,而不是POX的事。

5.2 让POX“说话”:日志、事件与抓包

调试POX应用时,我最大的心得就是:别急着改代码,先让程序把关键部位的信息打出来。

POX内部的core.getLogger()提供了一套标准的日志接口,可以在代码里加入log.info、log.warning、log.error。我在自己写的模块中,每一个关键路径都会加一些日志打印,比如收到PacketIn时打印源MAC、目的MAC、入端口和查表结果,这样每次实验跑起来,我看着日志就能把整个转发的决策过程还原出来,不需要猜。

如果你的问题是底层的协议交互异常,那就得靠抓包了。POX默认监听6633端口,你可以在服务器或本机上用tcpdump抓取这个端口的流量,观察交换机与控制器之间是否真的有openflow消息交互。抓包工具需要能识别OpenFlow协议,把6633端口加进去解析,不然看到的只是一团TCP数据。我抓包时关注三个时间点:交换机连接时有没有hello握手;主机发包时有没有PacketIn消息;控制器回复时有没有FlowMod或PacketOut。这三个时间点对应了SDN控制逻辑的完整链路。

关于日志级别,POX启动时还可以通过log.level --DEBUG参数提高日志级别,让内部组件输出更详细的事件信息。我调试拓扑发现问题时经常开DEBUG模式,日志量虽然很大,但信息量也比正常模式丰富得多。学习阶段多看看DEBUG日志,能帮你快速建立“事件从哪里来、到哪里去”的直觉。

5.3 常见问题速查表

我把学习过程中反复出现的问题整理成了一张速查表,放在下面供参考。每次遇到新问题,我建议都先对着这张表过一遍,再往更深的地方排查。

问题现象常见原因处理办法
POX启动时报端口被占用之前的控制器进程没有清掉用lsof -i:6633找到进程并结束,或重启机器
Mininet交换机连不上控制器--controller=remote参数没写对检查ip和port是否与POX监听地址一致
启动目录不在POX根目录组件导入失败,出现ImportError: No module named pox切到项目根目录再启动
能连上但ping不通控制器组件没加载或流表异常确认加载了l2_learning或自己的转发模块
日志里没有PacketIn事件交换机没有流量上送或控制器端口被防火墙拦截关闭无关防火墙规则,或检查Mininet主机连通性
流表不断增长不停匹配字段过宽或空闲超时太短适当增加匹配字段的唯一性,调长空闲超时
代码用Python3运行报语法错POX是Python2项目使用Python2解释器,或用容器运行
交换机连接后立刻断开控制器里抛异常或端口模式不匹配查看POX日志的traceback,修正异常后重启

这张表没有覆盖所有问题,但它覆盖了我见过的90%的POX学习初期故障。另外我特别建议你在排查时不只看POX日志,也要交叉看交换机侧的状态,比如dpctl show打印端口状态。很多时候控制器侧完全正常,问题出在交换机端口没有启用或虚拟网卡配置错误。

6. 后续可以怎么用POX做深一点的学习

6.1 从“会用组件”到“改造组件”的进阶路径

如果你已经能跑通内置组件、能写一个自己的l2_learning控制器,那说明你已经跨过了POX入门最陡的台阶。这时候我建议不要急着跳去学全新的框架,而是在POX内部做几个“主动改造”的实验,把理解再钉深一层。

第一个改造方向是给它加VLAN隔离。在原有MAC表里把键从“交换机ID+MAC”变成“交换机ID+VLAN+MAC”,PacketIn处理时从报文里取出VLAN标签,再执行同样的查表转发逻辑。做完这个改造,你就会发现SDN里实现网络隔离的本质是什么——无非是把查表时用的字段变多一些。

第二个方向是做一个简单的静态路由应用。用三台交换机组成一条链路,在控制器脚本里预先定义好每个子网的下一跳,收到PacketIn时按目标IP转发。虽然POX不是做三层路由的最佳选择,但通过它理解“控制器的路由决策如何转换成交换机流表”这一过程,比直接拿一个成熟路由应用来看要有用得多。

第三个方向是动手改拓扑发现后的自动转发路径选择。这个方向会牵涉到openflow.discovery提供的拓扑数据,属于进阶玩法。你可以试着在环型拓扑下,用控制器计算出一条路径,并把路径上的每个交换机都下发对应的流表项。我个人认为这个实验做完,你对“控制器如何管理全网流表”的理解就会发生质变。

6.2 从POX迁移到其他控制器时的思维迁移

最后聊一下怎么从POX平滑过渡到其他控制器。

很多人学完POX之后,会听说某个新控制器支持OpenFlow 1.3、有更强的REST API、支持集群部署,于是就想赶紧切换。这个想法本身没问题,但如果之前的基础没打好,迁移过程可能会比较痛苦。我的建议是:不要只看新特性,而是把你在POX里建立起的“事件、消息、组件”模型映射到新框架中。

比如学习一个新的控制器时,第一件事去找它的“ConnectionUp”等价物是什么,它的“PacketIn”处理函数在哪里注册,它用什么方法来下发流表。大多数控制器无论包装多复杂,最终都要回答这三个问题。你能在POX里回答出这三个问题,迁移到新环境时只需要熟悉新框架的名字和API,核心逻辑依然沿用你已有的SDN思考方式。

我在实际学习过程中也有一个体会:把同一个自学习交换机应用分别写在不同控制器下跑通,是快速理解多个框架差异的最好方法。写的时候你会注意到它们对事件处理的注册方式不同、流表消息构造方式不同、甚至对数据包的解析方式也不同,但整个设计思想是高度一致的。完成一次这样的对照实验,你对SDN控制器的理解就会全面得多。

如果未来要走得更远,还可以尝试用网络可视化工具把POX的拓扑发现结果渲染出来,或者把控制器的流表操作做成一个自动化脚本。反正POX本身足够简单,适合你在这个平台上反复折腾,每一次折腾都是在为后续更复杂的网络编程打基础。学习控制器这件事没有捷径,多写多跑多抓包,就一定能弄明白。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询