ryu控制器在SDN三层架构中是处于中间层,(如图1)

网管北向接口 kafka_sdn


图1 SDN三层架构

如何实现与应用层的通信,如图1所示,实现RYU控制器与应用层(如开发应用web界面时数据可视化平台)数据通信就需要利用SDN的北向接口。北向接口实现的功能就时某个应用软件实体与控制器中的数据的通信。本文,我们将用socket编程就能轻易实现应用层与RYU控制器之间的数据通信以及简单展示

实现拓扑逻辑原理
三、实现代码
服务端(在ubuntu上)
server端主要用于接收控制器的连接,并接收由控制器发送过来的信息,这里主要是dpid和port信息,也就是当主机h1、h2进行通信时,控制器收集通信时经过的端口和交换机id
具体代码:

import socket
import re


def main():
 server1 = socket.socket(socket.AF_INET, socket.SOCK_STREAM)  # TCP

 host = '10.0.0.2' #ubuntu地址,开启服务功能,用来给ryu主动连接提交数据
 port = 12345 #开放端口,自主定义一般大于1024
 server1.bind((host, port))

 server1.listen(5)
 while True:
     conn, addr = server1.accept()
     print("----------------------------")
     print("Success connect from ", addr)

     try:
         count = 0
         while True:
             data = conn.recv(1024)
             data = re.split(r'[, :]', data.decode('utf-8'))  # 对收到的信息进行解析,包括dpid和port
             count += 1
             print("from {0}:dpid={1}, in_port={2}".format(addr, data[0], data[1]))
         conn.close()
     except Exception as error:  # 当控制器和应用层断开连接后,输出统计信息
         print('共接收{}条信息。'.format(count-1))
         print(error)
         exit()


if __name__ == '__main__':
 main()

mininet拓扑代码

ryu

from ryu.base import app_manager
from ryu.controller.handler import set_ev_cls
from ryu.controller.handler import MAIN_DISPATCHER, CONFIG_DISPATCHER
from ryu.controller import ofp_event
from ryu.ofproto import ofproto_v1_3
import socket
 
 
class L2Switch(app_manager.RyuApp):
    OFP_VERSIONS = [ofproto_v1_3.OFP_VERSION]
 
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.dp_id = '0'
        self.in_port = '0'
        
        
        self.client1 = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        dst_host = '10.0.0.2'
        dst_port = 12345
        
        try:
            self.client1.connect((dst_host, dst_port))
        except Exception as error:
            print('Connect error:', error)
 
    def doflow(self, datapath, command, priority, match, actions):
        ofp = datapath.ofproto
        ofp_parser = datapath.ofproto_parser
        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
        command = ofp.OFPFC_ADD
        match = ofp_parser.OFPMatch()
        actions = [ofp_parser.OFPActionOutput(ofp.OFPP_CONTROLLER, ofp.OFPCML_NO_BUFFER)]
        self.doflow(datapath, command, 0, match, actions)
 
    @set_ev_cls(ofp_event.EventOFPPacketIn, MAIN_DISPATCHER)
    def packet_in_handler(self, ev):
        msg = ev.msg
        dp = msg.datapath
        ofp = dp.ofproto
        ofp_parser = dp.ofproto_parser
        self.dp_id = dp.id
 
       
        start = str(msg).index('oxm_fields') + 11
        end = str(msg).index('),reason')
        inport_str = str(msg)[start:end]
        instr = eval(inport_str)
        self.in_port = instr['in_port']
 
        actions = [ofp_parser.OFPActionOutput(ofp.OFPP_FLOOD)]
 
        data = None
        if msg.buffer_id == ofp.OFP_NO_BUFFER:
             data = msg.data
        print('id:{0}     in_port:{1}'.format(self.dp_id, self.in_port))
        
        info = str(self.dp_id) + ',' + str(self.in_port)
        self.client1.send(info.encode())
 
        out = ofp_parser.OFPPacketOut(
            datapath=dp, buffer_id=msg.buffer_id, in_port=self.in_port,
            actions=actions, data=data)
        dp.send_msg(out)

四、实验测试

注意启动步骤:(如图中编号)

网管北向接口 kafka_安全_02

扩展,如何把服务器上收集的数据存入一个文件

将数据存储为Excel文件(.xlsx),您可以使用第三方库如openpyxl来实现。以下是将数据存储到Excel文件的修改版本:

import openpyxl
import socket
import re

def main():
    server1 = socket.socket(socket.AF_INET, socket.SOCK_STREAM)  # TCP

    host = '10.0.0.2'  # ubuntu地址,开启服务功能,用来给ryu主动连接提交数据
    port = 12345  # 开放端口,自主定义一般大于1024
    server1.bind((host, port))

    server1.listen(5)

    # 创建Excel文件并写入表头
    workbook = openpyxl.Workbook()
    sheet = workbook.active
    sheet.append(['Address', 'DPID', 'In_Port'])  # 表头

    while True:
        conn, addr = server1.accept()
        print("----------------------------")
        print("Success connect from ", addr)

        try:
            count = 0
            while True:
                data = conn.recv(1024)
                data = re.split(r'[, :]', data.decode('utf-8'))  # 对收到的信息进行解析,包括dpid和port
                count += 1
                print("from {0}: dpid={1}, in_port={2}".format(addr, data[0], data[1]))

                # 将数据写入Excel文件
                sheet.append([addr, data[0], data[1]])

        except Exception as error:  # 当控制器和应用层断开连接后,输出统计信息
            print('共接收{}条信息。'.format(count - 1))
            print(error)
            workbook.save('data.xlsx')  # 保存Excel文件
            exit()
    if __name__ == '__main__':
    main()

在上述代码中,我们使用了openpyxl库来创建并写入Excel文件。在循环中,每次接收到数据时,将数据写入Excel文件的工作表中。最后,使用workbook.save方法保存Excel文件

方法2
可以使用Python的CSV模块来实现。以下是将数据存储到CSV文件的修改版本:

import csv
import socket
import re


def main():
    server1 = socket.socket(socket.AF_INET, socket.SOCK_STREAM)  # TCP

    host = '10.0.0.2'  # ubuntu地址,开启服务功能,用来给ryu主动连接提交数据
    port = 12345  # 开放端口,自主定义一般大于1024
    server1.bind((host, port))

    server1.listen(5)

    # 创建CSV文件并写入表头
    with open('data.csv', 'w', newline='') as csvfile:
        writer = csv.writer(csvfile)
        writer.writerow(['Address', 'DPID', 'In_Port'])  # 表头

        while True:
            conn, addr = server1.accept()
            print("----------------------------")
            print("Success connect from ", addr)

            try:
                count = 0
                while True:
                    data = conn.recv(1024)
                    data = re.split(r'[, :]', data.decode('utf-8'))  # 对收到的信息进行解析,包括dpid和port
                    count += 1
                    print("from {0}: dpid={1}, in_port={2}".format(addr, data[0], data[1]))

                    # 将数据写入CSV文件
                    writer.writerow([addr, data[0], data[1]])

            except Exception as error:  # 当控制器和应用层断开连接后,输出统计信息
                print('共接收{}条信息。'.format(count - 1))
                print(error)
                exit()
if __name__ == '__main__':
    main()

在上述代码中,我们使用了csv模块来创建并写入CSV文件。在循环中,每次接收到数据时,将数据写入CSV文件中。

网管北向接口 kafka_安全_03


在服务端所在文件的通目录下(默认情况)可以检测到有有新生成的文件data .csv用记事本打开可以看到相关的数据

网管北向接口 kafka_sdn_04

网管北向接口 kafka_安全_05