首页 > 其他分享 >说明文档

说明文档

时间:2022-11-26 19:12:04浏览次数:46  
标签:src dst self datapath 说明 文档 net port

拓扑建立及带宽时延数据获取

具体拓扑结构如下:

topo.py

点击查看代码
#!/usr/bin/env python

from mininet.net import Mininet
from mininet.node import Controller, RemoteController, OVSController
from mininet.node import CPULimitedHost, Host, Node
from mininet.node import OVSKernelSwitch, UserSwitch
from mininet.node import IVSSwitch
from mininet.cli import CLI
from mininet.log import setLogLevel, info
from mininet.link import TCLink, Intf
from subprocess import call
import mininet.net
import time
import io

#   通过mininet.net的iperf函数获取带宽和时延的数据
#   将获取的json数据写入新建立的txt文件以供前端调用
def topowork():
    tcp = net.iperf((h1,h2),l4Type='TCP')
    f = open('/home/jode/102299142/tcp.txt', 'w')
    f.write(str(tcp))
    f.close
    udp = net.iperf((h1,h2),l4Type='UDP')
    f = open('/home/jode/102299142/udp.txt', 'w')
    f.write(str(udp))
    f.close


   
if __name__ == '__main__':
    setLogLevel( 'info' )
    net = Mininet( topo=None,
                   build=False,
                   ipBase='10.0.0.0/8')
    
    #   添加控制器
    info( '*** Adding controller\n' )
    c0=net.addController(name='c0',
                      controller=RemoteController,
                      ip='127.0.0.1',
                      protocol='tcp',
                      port=6633)
    
    #   添加交换器
    info( '*** Add switches\n')
    s3 = net.addSwitch('s3', cls=OVSKernelSwitch, dpid='0000000000000003')
    s4 = net.addSwitch('s4', cls=OVSKernelSwitch, dpid='0000000000000004')
    s1 = net.addSwitch('s1', cls=OVSKernelSwitch, dpid='0000000000000001')
    s2 = net.addSwitch('s2', cls=OVSKernelSwitch, dpid='0000000000000002')
    
    #   添加主机
    info( '*** Add hosts\n')
    h6 = net.addHost('h6', cls=Host, ip='10.0.0.6', defaultRoute=None)
    h2 = net.addHost('h2', cls=Host, ip='10.0.0.2', defaultRoute=None)
    h4 = net.addHost('h4', cls=Host, ip='10.0.0.4', defaultRoute=None)
    h5 = net.addHost('h5', cls=Host, ip='10.0.0.5', defaultRoute=None)
    h3 = net.addHost('h3', cls=Host, ip='10.0.0.3', defaultRoute=None)
    h1 = net.addHost('h1', cls=Host, ip='10.0.0.1', defaultRoute=None)
    
    #   添加链路
    info( '*** Add links\n')
    net.addLink(h1, s1)
    net.addLink(h2, s1)
    net.addLink(h3, s1)
    net.addLink(h4, s3)
    net.addLink(s1, s2)
    net.addLink(s1, s4)
    net.addLink(s3, s2)
    net.addLink(s4, s3)
    net.addLink(s3, h5)
    net.addLink(s3, h6)


    info( '*** Starting network\n')
    net.build()
    info( '*** Starting controllers\n')
    for controller in net.controllers:
        controller.start()

    info( '*** Starting switches\n')
    net.get('s3').start([c0])
    net.get('s4').start([c0])
    net.get('s1').start([c0])
    net.get('s2').start([c0])

    info( '*** Post configure switches and hosts\n')
    
    #   调用topowork函数获取数据
    net.start()
    topowork()
    CLI(net)
    net.stop()
    

负载均衡

基于链路质量(时延)的最短路径转发
原理:首先获取网络拓扑,将网络的各个节点的连接关系以及端口记录到列表中,然后再获取网络拓扑将其节点和边写入networkx的有向图中,利用时延探测得到每条链路的实时时延,将时延作为边权重更新在有向图中,最后利用shortest_path()方法得到基于权重的最短路径,根据路径获得每一跳的数据转发的输出端口,从而使得控制器正确按照最短时延下发转发命令。

fzjh.py

点击查看代码
import time

from ryu.base import app_manager
from ryu.controller import ofp_event
from ryu.controller.handler import MAIN_DISPATCHER, DEAD_DISPATCHER, CONFIG_DISPATCHER, HANDSHAKE_DISPATCHER
from ryu.controller.handler import set_ev_cls
from ryu.lib import hub
from ryu.ofproto import ofproto_v1_3
from ryu.topology.switches import LLDPPacket
from ryu.base.app_manager import lookup_service_brick
from ryu.lib.packet import packet
from ryu.lib.packet import ethernet

# 导入这些主要是为了让网络链路中产生LLDP数据包,只有产生了LLDP数据报,才能进行LLDP时延探测
from ryu.topology.api import get_switch, get_link, get_host
from ryu.topology import event, switches

# networkx用于存储链路信息,本程序存储为一个有向图
import networkx as nx


class DelayDetector(app_manager.RyuApp):
    OFP_VERSIONS = [ofproto_v1_3.OFP_VERSION]

    def __init__(self, *args, **kwargs):
        super(DelayDetector, self).__init__(*args, *kwargs)
        self.name = 'delay_detector'
        self.switches = lookup_service_brick('switches')
        # 初始化networkx的有向图
        self.G = nx.DiGraph()
        self.topology_api_app = self

        # 存储网络拓扑的交换机id
        self.dpidSwitch = {}
        # 存储echo往返时延
        self.echoDelay = {}
        # 存储LLDP时延
        self.src_dstDelay = {}
        # 存储链路的时延,即LLDP时延-echo的时延,计算出的每条链路的时延
        self.link_Delay = {}

        # 存储源-目的-权重(时延)的列表,用于向有向图写入边信息
        self.links_src_dst = []
        # 存储整个链路各个节点之间的连接信息,包括源端口,
        # 例如s1-s2,通过s1的2端口连接,存储的信息即为:{’1-2‘:2}
        self.id_port = {}

        # 实现协程,进行时延的周期探测
        self.detector_thread = hub.spawn(self.detector)

    # 每间隔三秒,进行如下操作:
    # 控制器向交换机发送一次echo报文,用以获取往返时延
    # echo时延知道后,计算各个链路的时延,即LLDP-echo时延
    # 根据各个链路的时延更新网络拓扑有向图的每条边的权重
    def detector(self):
        while True:
            self.send_echo_request()
            self.link_delay()
            self.update_topo()
            hub.sleep(3)

    def add_flow(self, datapath, priority, match, actions):
        ofp = datapath.ofproto
        ofp_parser = datapath.ofproto_parser
        command = ofp.OFPFC_ADD
        inst = [ofp_parser.OFPInstructionActions(ofp.OFPIT_APPLY_ACTIONS, actions)]
        req = ofp_parser.OFPFlowMod(datapath=datapath, command=command,
                                    priority=priority, match=match, instructions=inst)
        datapath.send_msg(req)

    @set_ev_cls(ofp_event.EventOFPSwitchFeatures, CONFIG_DISPATCHER)
    def switch_features_handler(self, ev):
        msg = ev.msg
        datapath = msg.datapath
        ofp = datapath.ofproto
        ofp_parser = datapath.ofproto_parser

        # add table-miss
        match = ofp_parser.OFPMatch()
        actions = [ofp_parser.OFPActionOutput(ofp.OFPP_CONTROLLER, ofp.OFPCML_NO_BUFFER)]
        self.add_flow(datapath=datapath, priority=0, match=match, actions=actions)

    @set_ev_cls(ofp_event.EventOFPStateChange, [MAIN_DISPATCHER, DEAD_DISPATCHER])
    def state_change_handler(self, ev):
        datapath = ev.datapath
        if ev.state == MAIN_DISPATCHER:
            if not datapath.id in self.dpidSwitch:
                self.dpidSwitch[datapath.id] = datapath
        elif ev.state == DEAD_DISPATCHER:
            if datapath.id in self.dpidSwitch:
                del self.dpidSwitch[datapath.id]

    events = [event.EventSwitchEnter, event.EventSwitchLeave,
              event.EventSwitchReconnected,
              event.EventPortAdd, event.EventPortDelete,
              event.EventPortModify,
              event.EventLinkAdd, event.EventLinkDelete]

    # 获取网络链路拓扑
    # 即将节点和边信息写入有向图中,默认的权重为0
    @set_ev_cls(events)
    def get_topo(self, ev):
        switch_list = get_switch(self.topology_api_app)
        topo_switches = []
        # 得到每个设备的id,并写入图中作为图的节点
        for switch in switch_list:
            topo_switches.append(switch.dp.id)
        self.G.add_nodes_from(topo_switches)

        link_list = get_link(self.topology_api_app)
        self.links_src_dst = []
        # 将得到的链路的信息作为边写入图中
        # 注意这里links_src_dst时列表里列表,即[[],[],[]],不能是元组,因为元组不可更改,也就是后面无法更新权重信息
        for link in link_list:
            self.links_src_dst.append([link.src.dpid, link.dst.dpid, 0])
        self.G.add_weighted_edges_from(self.links_src_dst)

        for link in link_list:
            self.links_src_dst.append([link.dst.dpid, link.src.dpid, 0])
        self.G.add_weighted_edges_from(self.links_src_dst)

    # 更新拓扑信息,主要更新有向图的边的权重
    # 即,程序获取链路的实时时延,当时延变化时,就将新的时延作为权重写入有向图中
    def update_topo(self):
        # 将link_Delay的时延作为权重更新进links_src_dst列表中,然后更新入有向图
        for key in self.link_Delay:
            list = key.split('-')
            l = (int(list[0]), int(list[2]))
            for i in self.links_src_dst:
                if l == (i[0], i[1]):
                    i[2] = self.link_Delay[key]

        self.G.add_weighted_edges_from(self.links_src_dst)

    # 获取输出的端口,这里的输出端口是控制器指示数据转发时按照最短权重获得的输出端口进行数据转发
    def get_out_port(self, datapath, src, dst, in_port):
        global out_port
        dpid = datapath.id

        # 开始时,各个主机可能在图中不存在,因为开始ryu只获取了交换机的dpid,并不知道各主机的信息,
        # 所以需要将主机存入图中
        # 同时将记录主机和交换机之间的连接关系和端口
        if src not in self.G:
            self.G.add_node(src)
            self.G.add_weighted_edges_from([[dpid, src, 0]])
            self.G.add_weighted_edges_from([[src, dpid, 0]])
            src_dst = "%s-%s" % (dpid, src)
            self.id_port[src_dst] = in_port

        # 计算出基于最小权重的链路,按照这个转发链路进行数据的转发
        if dst in self.G:
            path = nx.shortest_path(self.G, src, dst, weight='weight')
            next_hop = path[path.index(dpid) + 1]
            for key in self.id_port:
                match_key = "%s-%s" % (dpid, next_hop)
                if key == match_key:
                    out_port = self.id_port[key]
                    # print('key_out_port:', out_port)
            print(path)
        else:
            out_port = datapath.ofproto.OFPP_FLOOD
        return out_port

    # 由控制器向交换机发送echo报文,同时记录此时时间
    def send_echo_request(self):
        # 循环遍历交换机,逐一向存在的交换机发送echo探测报文
        for datapath in self.dpidSwitch.values():
            parser = datapath.ofproto_parser
            echo_req = parser.OFPEchoRequest(datapath, data=bytes("%.12f" % time.time(), encoding="utf8"))  # 获取当前时间

            datapath.send_msg(echo_req)
            # 每隔0.5秒向下一个交换机发送echo报文,防止回送报文同时到达控制器
            hub.sleep(0.5)

    # 交换机向控制器的echo请求回应报文,收到此报文时,控制器通过当前时间-时间戳,计算出往返时延
    @set_ev_cls(ofp_event.EventOFPEchoReply, [MAIN_DISPATCHER, CONFIG_DISPATCHER, HANDSHAKE_DISPATCHER])
    def echo_reply_handler(self, ev):
        now_timestamp = time.time()
        try:
            echo_delay = now_timestamp - eval(ev.msg.data)
            # 将交换机对应的echo时延写入字典保存起来
            self.echoDelay[ev.msg.datapath.id] = echo_delay
        except Exception as error:
            print("echo error:", error)
            return

    # 处理由交换机到来的消息,如LLDP消息和数据转发的消息
    @set_ev_cls(ofp_event.EventOFPPacketIn, MAIN_DISPATCHER)
    def packet_in_handler(self, ev):
        msg = ev.msg
        datapath = msg.datapath
        ofp = datapath.ofproto
        ofp_parser = datapath.ofproto_parser
        dpid = datapath.id
        in_port = msg.match['in_port']

        pkt = packet.Packet(msg.data)
        eth = pkt.get_protocols(ethernet.ethernet)[0]

        dst = eth.dst
        src = eth.src

        # try...except,由于packetin中存在LLDP消息和主机的数据转发消息,
        # 二者格式不一样,所以用try...except进行控制,分别处理两种消息;
        try:  # 处理到达的LLDP报文,从而获得LLDP时延
            src_dpid, src_outport = LLDPPacket.lldp_parse(msg.data)  # 获取两个相邻交换机的源交换机dpid和port_no(与目的交换机相连的端口)
            dst_dpid = msg.datapath.id  # 获取目的交换机(第二个),因为来到控制器的消息是由第二个(目的)交换机上传过来的
            if self.switches is None:
                self.switches = lookup_service_brick("switches")  # 获取交换机模块实例

            # 获得key(Port类实例)和data(PortData类实例)
            for port in self.switches.ports.keys():  # 开始获取对应交换机端口的发送时间戳
                if src_dpid == port.dpid and src_outport == port.port_no:  # 匹配key
                    port_data = self.switches.ports[port]  # 获取满足key条件的values值PortData实例,内部保存了发送LLDP报文时的timestamp信息
                    timestamp = port_data.timestamp
                    if timestamp:
                        delay = time.time() - timestamp
                        self._save_delay_data(src=src_dpid, dst=dst_dpid, src_port=src_outport, lldp_dealy=delay)
        except Exception as error:  # 处理到达的主机的转发消息
            out_port = self.get_out_port(datapath, src, dst, in_port)
            actions = [ofp_parser.OFPActionOutput(out_port)]

            # 这里如果使用add_flow()进行了流表的添加,那么程序中的实时更新拓扑的权重就无意义了,转发就会依据流表进行
            # 所以这里不使用add_flow()方法,而是采用hub的形式,也就是每次转发都会请求控制器进行实时计算链路质量

            # 如果执行的动作不是flood,那么此时应该依据流表项进行转发操作,所以需要添加流表到交换机
            # if out_port != ofp.OFPP_FLOOD:
            #     match = ofp_parser.OFPMatch(in_port=in_port, eth_dst=dst, eth_src=src)
            #     self.add_flow(datapath=datapath, priority=1, match=match, actions=actions)

            data = None
            if msg.buffer_id == ofp.OFP_NO_BUFFER:
                data = msg.data
            # 控制器指导执行的命令
            out = ofp_parser.OFPPacketOut(datapath=datapath, buffer_id=msg.buffer_id,
                                          in_port=in_port, actions=actions, data=data)
            datapath.send_msg(out)

    # 用于存储各个LLDP的时延
    # 同时记录拓扑中各个交换机之间的连接
    def _save_delay_data(self, src, dst, src_port, lldp_dealy):
        key = "%s-%s-%s" % (src, src_port, dst)
        src_dst = "%s-%s" % (src, dst)
        self.id_port[src_dst] = src_port
        # {'1-2': 2, '3-2': 2, '2-1': 2, '2-3': 3}
        self.src_dstDelay[key] = lldp_dealy

    # 计算链路的时延
    def link_delay(self):
        for key in self.src_dstDelay:
            list = key.split('-')
            t1 = 0
            t2 = 0
            for key_s in self.echoDelay:
                if key_s == int(list[0]):
                    t1 = self.echoDelay[key_s]
                if key_s == int(list[2]):
                    t2 = self.echoDelay[key_s]
            delay = self.src_dstDelay[key] - (t1 + t2) / 2
            # 由于误差及其他原因,可能出现时延为负值情况,如果为负值就不要进行时延的更新
            if delay >= 0:
                self.link_Delay[key] = self.src_dstDelay[key] - (t1 + t2) / 2
            else:
                continue

标签:src,dst,self,datapath,说明,文档,net,port
From: https://www.cnblogs.com/hjc13112580/p/16928083.html

相关文章

  • scrollTop 用法说明
    scrollTop属性是什么?有些情况下,“元素中内容”的高度会超过“元素本身”的高度,下面的演示中,外层元素的高度值是200px,内层元素的高度值是300px。很明显,“外层元素中的......
  • jQuery 1.4官方文档中文版
    PeanutButterJelly").sortable().bind("endsort",function(){$(":text.food").val(function(){return$("ul.sortableli:eq("+$(this).attr("data-index")+")......
  • 基于springboot财务管理系统设计与实现的源码+文档
    摘 要随着信息技术和网络技术的飞速发展,人类已进入全新信息化时代,传统管理技术已无法高效,便捷地管理信息。为了迎合时代需求,优化管理效率,各种各样的管理系统应运而生,各行......
  • 基于springboot大学生体质测试管理系统设计与实现的源码+文档
    摘要大学生体质测试管理系统提供给用户一个简单方便体质测试管理信息,通过留言区互动更方便。本系统采用了B/S体系的结构,使用了java技术以及MYSQL作为后台数据库进行开发。......
  • 基于springboot电动车智能充电服务平台设计与实现的源码+文档
    摘要在Internet高速发展的今天,我们生活的各个领域都涉及到计算机的应用,其中包括电动车智能充电服务平台的网络应用,在外国电动车智能充电服务平台已经是很普遍的方式,不过国......
  • mt7688 接口网卡接口说明
    硬件平台:mt7688软件平台:原生sdkMediaTek_ApSoC_SDK_4300_201409161.使用 ifconfig-a 命令可以列出mt7688上面所有的逻辑网卡接口,常见接口如下:apcli0:Thewirelessa......
  • 基于springboot财务管理系统设计与实现的源码+文档
     摘要随着信息化时代的到来,管理系统都趋向于智能化、系统化,车辆充电桩管理系统也不例外,但目前国内仍都使用人工管理,市场规模越来越大,同时信息量也越来越庞大,人工管理显然......
  • 【爬虫】bs4搜索文档,css选择器,selenium基本使用
    目录1.bs4搜索文档树1.1字符串:可以按照标签名,属性名查找1.2正则表达式标签名,属性可以使用正则匹配1.3列表标签名,属性名等于列表或条件1.4True标签名,属性名......
  • word文档翻译免费的方法?翻译word的方法分享!​
    word文档翻译免费的方法?很多小伙伴在处理word文档的时候都得心应手,但是对于需要翻译的word文档却手足无措,总不能全部复制下来,在进行翻译吧,不仅仅费时费力,而且对于这么多的......
  • pageOffice控件实现 office word文档在线编辑填充指定数据
    pageOffice控件实现officeword文档在线编辑填充指定数据应用场景OA办公中,经常要在文档的指定位置,填充后端指定数据。如合同中,姓名位置,金额位置,住址位置,要填充后端指......