概述
本实验实用平台cloudlab,cloudlab可以提供多台虚拟机/真机,是实验的物理载体
实验目的是让不同的tcp视频流通过switch1和switch2时被分流到不同的路径上(via node1/node2/node3)
实验前,我调查了收发/处理视频流的一些常用的工具,并看了前人做的一些实验,发现rtmp-nginx很适合本实验
表格一:linux处理视频流的相关软件:
名称 | 描述 | 是否适用 |
ffmpeg | 多媒体视频处理工具,用于视频采集、视频格式处理、视频流化 | 后面会用 |
vlc | 简单、快速、强大的流媒体播放器,无需安装解码器包的媒体播放器 | 不用 |
WebRTC | 网页即时通信,支持网页浏览器进行实时语音对话或视频对话的api,google发起的实时通信解决方案,包括音视频采集、编解码、数据传输、音视频展示等功能,基于udp,可以代替rtmp,提供了一套api,可能需要二次开发,不支持cdn | 不用 |
rtmp | 主流的流媒体传输协议,基于tcp,协议简单,在各平台上容易实现,比较容易找到教程,支持cdn | 适用 |
nginx | web-server,和rtmp配套使用 | 适用 |
(1)cloudlab上搭起topo
#每个节点在每个link上各有一个ip,switch节点有4个网卡,4个ip,node节点有两个网卡,两个ip
#client的ip是唯一的,是10.10.1.1
#server的ip是唯一的,是10.10.8.2
client(10.10.1.1) ————— switch1(10.10.1.2)
switch1(10.10.2.1) ————— node1(10.10.2.2)
switch1(10.10.3.1) ————— node2(10.10.3.2)
switch1(10.10.4.1) ————— node3(10.10.4.2)
node1(10.10.5.1) ————— switch2(10.10.5.2)
node2(10.10.6.2) ————— switch2(10.10.6.1)
node3(10.10.7.1) ————— switch2(10.10.7.2)
switch2(10.10.8.1) ————— server(10.10.8.2)
(2)登陆"server"节点,把它设置为rtmp server
wget http://nginx.org/download/nginx-1.17.3.tar.gz
git clone https://github.com/arut/nginx-rtmp-module
tar -zxf nginx-1.17.3.tar.gz
cd nginx-1.17.3
./configure --add-module=/root/nginx-rtmp-module
#此时发生缺少pcre library错误
#需要:
apt-get update
apt-get install libpcre3 libpcre3-dev
apt-get install openssl libssl-dev
sudo apt-get install zlib1g-dev
#再重新执行configure文件
./configure --add-module=/root/nginx-rtmp-module
make
make install
cd /usr/local/nginx
cd conf
#在本地写好nginx.conf文件(代码见后面),并上传到server上,覆盖掉原来的nginx.conf文件
scp ~/Desktop/nginx.conf root@155.98.36.44:/usr/local/nginx/conf
cd ..
./sbin/nginx -c conf/nginx.conf
cd html
mkdir play
#nginx.conf:
worker_processes 1;
events {
worker_connections 1024;
}
rtmp {
server {
listen 1935;
chunk_size 4000;
application play {
play /usr/local/nginx/html/play;
}
application hls {
live on;
hls on;
hls_path /usr/local/nginx/html/hls;
hls_fragment 1s;
hls_playlist_length 4s;
}
application live {
live on;
}
}
}
http {
include mime.types;
default_type application/octet-stream;
sendfile on;
keepalive_timeout 65;
server {
listen 8000;
server_name localhost;
location /stat {
rtmp_stat all;
rtmp_stat_stylesheet stat.xsl;
}
location /stat.xsl {
# XML stylesheet to view RTMP stats.
# Copy stat.xsl wherever you want
# and put the full directory path here
root /usr/local/nginx-rtmp-module;
}
location /hls {
# Serve HLS fragments
types {
application/vnd.apple.mpegurl m3u8;
video/mp2t ts;
}
root /usr/local/nginx/html;
add_header Cache-Control no-cache;
}
}
}
打开本地shell,将本地的mp4文件上传到rtmp server,作为视频流的视频
scp ~/Desktop/6mins.mp4 root@155.98.36.44:/usr/local/nginx/html/play
scp ~/Desktop/3mins.mp4 root@155.98.36.44:/usr/local/nginx/html/play
至此,rtmp server设置完成
(3) 登陆client节点,设置rtmp client
apt-get update
apt install rtmpdump
#设置完测试一下:
rtmpdump -r rtmp://10.10.8.1:1935/play/3mins.mp4
rtmpdump -r rtmp://10.10.8.1:1935/play/3mins.mp4
使用rtmpdump测试一下能不能顺利接收到视频流
可以接收到:(可以在shell里看到乱码)
此时,有关rtmp的设置已经设置完成了,接下来设置ovs
(3)登陆switch1,switch2两个节点,在上面下载、安装software ovs
这里略,前面的博客里有写怎么安装
在switch1上搭起br1
#搭起switch(bridge)
ovs-vsctl add-br br1
#绑定四个物理端口,每个真机的物理端口名称不太一样,这里用eth1234代替
ovs-vsctl add-port br1 eth1
ovs-vsctl add-port br1 eth2
ovs-vsctl add-port br1 eth3
ovs-vsctl add-port br1 eth4
#物理网卡绑定到bridge后,它的ip会失效,即无法再使用ip地址访问到四个物理网卡
#绑定四个虚拟端口,作为ip地址的载体:
ovs-vsctl add-port br1 vp1 -- set Interface vp1 type=internal
ovs-vsctl add-port br1 vp2 -- set Interface vp2 type=internal
ovs-vsctl add-port br1 vp3 -- set Interface vp3 type=internal
ovs-vsctl add-port br1 vp4 -- set Interface vp4 type=internal
#给四个虚拟网卡设置ip地址
ifconfig vp1 10.10.1.2
ifconfig vp2 10.10.2.1
ifconfig vp3 10.10.3.1
ifconfig vp4 10.10.4.1
#给四个物理网卡的ip设置为0
#在进行此步骤之前务必先记录下哪个物理网卡对应哪个虚拟网卡,后面会用到
#记录的方式可以参照后面的表格2
ifconfig eth1 0.0.0.0
ifconfig eth2 0.0.0.0
ifconfig eth3 0.0.0.0
ifconfig eth4 0.0.0.0
#将br1的dpid设置为1(方便写controller文件)
ovs-vsctl set bridge br1 other-config:datapath-id=0000000000000001
在switch2上搭起br2,步骤和switch1上的完全一样
#搭起switch(bridge)
ovs-vsctl add-br br2
#绑定四个物理端口,每个真机的物理端口名称不太一样,这里用eth1234代替
ovs-vsctl add-port br2 eth1
ovs-vsctl add-port br2 eth2
ovs-vsctl add-port br2 eth3
ovs-vsctl add-port br2 eth4
#物理网卡绑定到bridge后,它的ip会失效,即无法再使用ip地址访问到四个物理网卡
#绑定四个虚拟端口,作为ip地址的载体:
ovs-vsctl add-port br2 vp1 -- set Interface vp1 type=internal
ovs-vsctl add-port br2 vp2 -- set Interface vp2 type=internal
ovs-vsctl add-port br2 vp3 -- set Interface vp3 type=internal
ovs-vsctl add-port br2 vp4 -- set Interface vp4 type=internal
#给四个虚拟网卡设置ip地址
ifconfig vp1 10.10.5.2
ifconfig vp2 10.10.6.1
ifconfig vp3 10.10.7.2
ifconfig vp4 10.10.8.2
#给四个物理网卡的ip设置为0
#在进行此步骤之前务必先记录下哪个物理网卡对应哪个虚拟网卡,后面会用到
#记录的方式可以参照后面的表格2
ifconfig eth1 0.0.0.0
ifconfig eth2 0.0.0.0
ifconfig eth3 0.0.0.0
ifconfig eth4 0.0.0.0
#将br1的dpid设置为1(方便写controller文件)
ovs-vsctl set bridge br2 other-config:datapath-id=0000000000000002
两个switch基本搭起了,现在两个switch上没有任何流表项,可能会出一些环路上的问题,先不管,先把controller搭一下
表格2:
#第一列是物理网卡名字
#第二列是物理网课的mac
#第三列是在没修改之前物理网课对应的ip
#第四列是我们设置的每个物理网课对应的虚拟网卡
#第五列是虚拟网卡vp*对应的mac地址
#第六列是物理网卡enp*****对应的绑定到br1br2时br1br2发给它的port-number,可以用ovs-ofctl show br1br2来查看
switch1 | |||||
enp6s0f1 | 3c:fd:fe:05:8f:e2 | 10.10.1.2 | vp1 | 26:91:67:e5:fc:90 | 1 |
enp4s0f0 | a0:36:9f:96:45:12 | 10.10.2.1 | vp2 | 2a:09:31:bd:fc:8a | 2 |
enp6s0f2 | 3c:fd:fe:05:8f:e4 | 10.10.3.1 | vp3 | 22:57:c4:79:4c:e6 | 3 |
enp6s0f0 | 3c:fd:fe:05:8f:e0 | 10.10.4.1 | vp4 | e6:cc:13:b9:9a:34 | 4 |
switch2 | |||||
enp6s0f1 | 3c:fd:fe:05:ac:42 | 10.10.5.2 | vp1 | 42:57:e5:32:e3:38 | 1 |
enp6s0f0 | 3c:fd:fe:05:ac:40 | 10.10.6.1 | vp2 | 86:76:4f:20:ba:a5 | 2 |
enp6s0f2 | 3c:fd:fe:05:ac:44 | 10.10.7.2 | vp3 | 7e:79:7f:dc:ab:b7 | 3 |
enp6s0f3 | 3c:fd:fe:05:ac:46 | 10.10.8.2 | vp4 | ae:87:88:0a:b7:83 | 4 |
(4)登陆controller节点,搭起ryu controller
ryu环境的搭建过程在前面安装ovs的那个博客里有,略
写controller文件:
这个文件里把tcp流发向不同的link的主要逻辑在handle_tcp里,这里设置了一句计数器,每次增加1,再经过mod操作,把流分往不同的link
from ryu.base import app_manager
from ryu.controller import ofp_event
from ryu.controller.handler import CONFIG_DISPATCHER, MAIN_DISPATCHER
from ryu.controller.handler import set_ev_cls
from ryu.ofproto import ofproto_v1_3
from ryu.ofproto import ether
from ryu.ofproto import inet
from ryu.lib.packet import packet
from ryu.lib.packet import ethernet
from ryu.lib.packet import arp
from ryu.lib.packet import ipv4
from ryu.lib.packet import icmp
from ryu.lib.packet import tcp
import random
class ExampleSwitch13(app_manager.RyuApp):
OFP_VERSIONS = [ofproto_v1_3.OFP_VERSION]
def __init__(self, *args, **kwargs):
super(ExampleSwitch13, self).__init__(*args, **kwargs)
self.mac = {"client":"3c:fd:fe:05:ce:c2","server":"3c:fd:fe:05:94:c2","s1v1":"26:91:67:e5:fc:90","s1v2":"2a:09:31:bd:fc:8a","s1v3":"22:57:c4:79:4c:e6","s1v4":"e6:cc:13:b9:9a:34","s2v1":"42:57:e5:32:e3:38","s2v2":"86:76:4f:20:ba:a5","s2v3":"7e:79:7f:dc:ab:b7","s2v4":"ae:87:88:0a:b7:83","node1l":"a0:36:9f:6b:fd:4c","node1r":"3c:fd:fe:05:7b:02","node2l":"3c:fd:fe:05:de:82","node2r":"3c:fd:fe:05:de:80","node3l":"3c:fd:fe:05:49:62","node3r":"3c:fd:fe:05:49:60"}
self.ip = {"client":"10.10.1.1","server":"10.10.8.1","s1v1":"10.10.1.2","s1v2":"10.10.2.1","s1v3":"10.10.3.1","s1v4":"10.10.4.1","s2v1":"10.10.5.2","s2v2":"10.10.6.1","s2v3":"10.10.7.2","s2v4":"10.10.8.2"}
self.v2e = {"s1v1":1,"s1v2":2,"s1v3":3,"s1v4":4,"s2v1":1,"s2v2":2,"s2v3":3,"s2v4":4}
self.s2s = {"s1":{"client":"s1v1","node1":"s1v2","node2":"s1v3","node3":"s1v4"},"s2":{"server":"s2v4","node1":"s2v1","node2":"s2v2","node3":"s2v3"}}
self.counter = 0
@set_ev_cls(ofp_event.EventOFPSwitchFeatures, CONFIG_DISPATCHER)
def switch_features_handler(self, ev):
mac, ip = self.mac, self.ip
v2e, s2s = self.v2e, self.s2s
datapath = ev.msg.datapath
ofproto = datapath.ofproto
parser = datapath.ofproto_parser
# install the table-miss flow entry.
match = parser.OFPMatch()
actions = [parser.OFPActionOutput(ofproto.OFPP_CONTROLLER, ofproto.OFPCML_NO_BUFFER)]
self.add_flow(datapath, 0, match, actions)
dpid = datapath.id
print("******setting features for s" + str(dpid) + "******")
self.send_set_config(datapath)
if dpid == 1:
#icmp:
#client -> server
inport, outport, toport = s2s["s1"]["client"], s2s["s1"]["node1"], "node1l"
self.add_icmp_rules(datapath,v2e[inport],v2e[outport] ,ip["client"],ip["server"], mac[outport], mac[toport])
#server -> client
inport, outport, toport = s2s["s1"]["node3"], s2s["s1"]["client"], "client"
self.add_icmp_rules(datapath,v2e[inport],v2e[outport] ,ip["server"],ip["client"], mac[outport], mac[toport])
#tcp:
#server -> client
inport, outport, toport = s2s["s1"]["node1"], s2s["s1"]["client"], "client"
self.add_tcp_rules(datapath,v2e[inport],v2e[outport] ,ip["server"],ip["client"], mac[outport], mac[toport])
inport, outport, toport = s2s["s1"]["node2"], s2s["s1"]["client"], "client"
self.add_tcp_rules(datapath,v2e[inport],v2e[outport] ,ip["server"],ip["client"], mac[outport], mac[toport])
inport, outport, toport = s2s["s1"]["node3"], s2s["s1"]["client"], "client"
self.add_tcp_rules(datapath,v2e[inport],v2e[outport] ,ip["server"],ip["client"], mac[outport], mac[toport])
elif dpid == 2:
#icmp:
#client -> server
inport, outport, toport = s2s["s2"]["node1"], s2s["s2"]["server"], "server"
self.add_icmp_rules(datapath,v2e[inport],v2e[outport] ,ip["client"],ip["server"], mac[outport], mac[toport])
#server -> client
inport, outport, toport = s2s["s2"]["server"], s2s["s2"]["node3"], "node3r"
self.add_icmp_rules(datapath,v2e[inport],v2e[outport] ,ip["server"],ip["client"], mac[outport], mac[toport])
#tcp:
#client -> server:
inport, outport, toport = s2s["s2"]["node1"], s2s["s2"]["server"], "server"
self.add_tcp_rules(datapath,v2e[inport],v2e[outport] ,ip["client"],ip["server"], mac[outport], mac[toport])
inport, outport, toport = s2s["s2"]["node2"], s2s["s2"]["server"], "server"
self.add_tcp_rules(datapath,v2e[inport],v2e[outport] ,ip["client"],ip["server"], mac[outport], mac[toport])
inport, outport, toport = s2s["s2"]["node3"], s2s["s2"]["server"], "server"
self.add_tcp_rules(datapath,v2e[inport],v2e[outport] ,ip["client"],ip["server"], mac[outport], mac[toport])
#下发流表逻辑
def add_flow(self, datapath, priority, match, actions):
ofproto = datapath.ofproto
parser = datapath.ofproto_parser
# construct flow_mod message and send it.
inst = [parser.OFPInstructionActions(ofproto.OFPIT_APPLY_ACTIONS, actions)]
mod = parser.OFPFlowMod(datapath=datapath, priority=priority, match=match, instructions=inst)
datapath.send_msg(mod)
#对icmp单独的转发规则:h1-s1-s3-s4-h2/h2-s4-s3-s1-h1
def add_icmp_rules(self, datapath, in_port, out_port, ips, ipd, macs, macd):
priority = 20
parser = datapath.ofproto_parser
actions = [parser.OFPActionSetField(eth_src=macs),parser.OFPActionSetField(eth_dst=macd), parser.OFPActionOutput(out_port)]
match = parser.OFPMatch(eth_type =ether.ETH_TYPE_IP,ip_proto=inet.IPPROTO_ICMP, in_port= in_port, ipv4_src=ips, ipv4_dst=ipd)
self.add_flow(datapath, priority, match, actions)
def add_tcp_rules(self, datapath, in_port, out_port, ips, ipd, macs, macd):
priority = 20
parser = datapath.ofproto_parser
actions = [parser.OFPActionSetField(eth_src=macs),parser.OFPActionSetField(eth_dst=macd), parser.OFPActionOutput(out_port)]
match = parser.OFPMatch(eth_type =ether.ETH_TYPE_IP,ip_proto=inet.IPPROTO_TCP, in_port= in_port, ipv4_src=ips, ipv4_dst=ipd)
self.add_flow(datapath, priority, match, actions)
def send_set_config(self, datapath):
#把Normal先安装给datapath
ofp = datapath.ofproto
ofp_parser = datapath.ofproto_parser
req = ofp_parser.OFPSetConfig(datapath, ofp.OFPC_FRAG_NORMAL, 256)
datapath.send_msg(req)
@set_ev_cls(ofp_event.EventOFPPacketIn, MAIN_DISPATCHER)
def _pakcet_in_handler(self, ev):
#如果发生了table-miss,就把对应的default forwarding安装上
#如果miss的是icmp,就把对应的icmp forwarding安装上
datapath = ev.msg.datapath
ofproto = datapath.ofproto
parser = datapath.ofproto_parser
dpid = datapath.id
msg = ev.msg
print("****** receiving a packet in from " + str(dpid) + " ******")
pkt = packet.Packet(msg.data)
eth_pkt = pkt.get_protocol(ethernet.ethernet)
in_port = msg.match["in_port"]
ethertype = eth_pkt.ethertype
if not eth_pkt:
return
pkt_arp = pkt.get_protocol(arp.arp)
if ethertype == ether.ETH_TYPE_ARP:
self.handle_arp(datapath, in_port, pkt)
return
if ethertype == ether.ETH_TYPE_IP:
self.handle_ip(datapath, in_port, pkt)
return
def handle_arp(self, datapath, in_port, pkt):
print("*************** handling arp packet in from "+str(datapath.id)+" ****************")
ofproto = datapath.ofproto
parser = datapath.ofproto_parser
# parse out the ethernet and arp packet
eth_pkt = pkt.get_protocol(ethernet.ethernet)
arp_pkt = pkt.get_protocol(arp.arp)
# obtain the MAC of dst IP
dpid = datapath.id
if dpid == 1:#sw1上的arp请求
arp_resolv_mac = self.mac["s1v1"]
elif dpid == 2:#sw2上的arp请求
arp_resolv_mac = self.mac["s2v4"]
else:
print("*** require handling arp on switch: dpid="+str(dpid)+" *****")
return
### generate the ARP reply msg, please refer RYU documentation
### the packet library section
# ARP Reply Msg
ether_hd = ethernet.ethernet(dst = eth_pkt.src,
src = arp_resolv_mac,
ethertype = ether.ETH_TYPE_ARP);
arp_hd = arp.arp(hwtype=1, proto = 2048, hlen = 6, plen = 4,
opcode = 2, src_mac = arp_resolv_mac,
src_ip = arp_pkt.dst_ip, dst_mac = eth_pkt.src,
dst_ip = arp_pkt.src_ip);
arp_reply = packet.Packet()
arp_reply.add_protocol(ether_hd)
arp_reply.add_protocol(arp_hd)
arp_reply.serialize()
# send the Packet Out mst to back to the host who is initilaizing the ARP
actions = [parser.OFPActionOutput(in_port)];
out = parser.OFPPacketOut(datapath, ofproto.OFP_NO_BUFFER,
ofproto.OFPP_CONTROLLER, actions,
arp_reply.data)
print("************** sending arp packet out **************")
datapath.send_msg(out)
def handle_ip(self, datapath, in_port, pkt):
ipv4_pkt = pkt.get_protocol(ipv4.ipv4)
if ipv4_pkt.proto == inet.IPPROTO_ICMP:
self.handle_icmp(datapath, in_port, pkt)
elif ipv4_pkt.proto == inet.IPPROTO_TCP:
self.handle_tcp(datapath, in_port, pkt)
def handle_tcp(self, datapath, in_port, pkt):
print("************* handling tcp packet in ****************")
ofproto = datapath.ofproto
parser = datapath.ofproto_parser
mac = self.mac
v2e, s2s = self.v2e, self.s2s
ipv4_pkt = pkt.get_protocol(ipv4.ipv4)
src_ip = ipv4_pkt.src
dst_ip = ipv4_pkt.dst
tcp_pkt = pkt.get_protocol(tcp.tcp)
src_port, dst_port = tcp_pkt.src_port, tcp_pkt.dst_port
self.counter += 1
rand = self.counter % 3
inp = in_port
if datapath.id == 1:
if inp != v2e[s2s["s1"]["client"]]:
macs = mac[s2s["s1"]["client"]]
macd = mac["client"]
oup = v2e[s2s["s1"]["client"]]
elif rand == 0:
macs = mac[s2s["s1"]["node1"]]
macd = mac["node1l"]
oup = v2e[s2s["s1"]["node1"]]
elif rand == 1:
macs = mac[s2s["s1"]["node2"]]
macd = mac["node2l"]
oup = v2e[s2s["s1"]["node2"]]
else:
macs = mac[s2s["s1"]["node3"]]
macd = mac["node3l"]
oup = v2e[s2s["s1"]["node3"]]
elif datapath.id == 2:
if inp != v2e[s2s["s2"]["server"]]:
macs = mac[s2s["s2"]["server"]]
macd = mac["server"]
oup = v2e[s2s["s2"]["server"]]
elif rand == 0:
macs = mac[s2s["s2"]["node1"]]
macd = mac["node1r"]
oup = v2e[s2s["s2"]["node1"]]
elif rand == 1:
macs = mac[s2s["s2"]["node2"]]
macd = mac["node2r"]
oup = v2e[s2s["s2"]["node2"]]
else:
macs = mac[s2s["s2"]["node3"]]
macd = mac["node3r"]
oup = v2e[s2s["s2"]["node3"]]
else:
return
match = parser.OFPMatch(eth_type = ether.ETH_TYPE_IP,
ipv4_src = src_ip,
ipv4_dst = dst_ip,
tcp_dst = dst_port,
tcp_src = src_port,
ip_proto = inet.IPPROTO_TCP)
actions = [parser.OFPActionSetField(eth_src=macs),parser.OFPActionSetField(eth_dst=macd),parser.OFPActionOutput(oup)]
self.add_flow(datapath, 50, match, actions)
def handle_icmp(self, datapath, in_port, pkt):
print("************* handling icmp packet in ****************")
#对于任意不是h1/h2互相传的icmp,这里的处理是switch一旦拿到这样的match不到的icmp packet,controller直接给一个回复(有时间想想是不是有更好的方法)
ofproto = datapath.ofproto
parser = datapath.ofproto_parser
ipv4_pkt = pkt.get_protocol(ipv4.ipv4)
src_ip, dst_ip = ipv4_pkt.src, ipv4_pkt.dst
eth_pkt = pkt.get_protocol(ethernet.ethernet)
icmp_pkt = pkt.get_protocol(icmp.icmp)
#ethernet源和目的互换
ether_hd = ethernet.ethernet(dst = eth_pkt.src, src = eth_pkt.dst, ethertype = ether.ETH_TYPE_IP)
ipv4_hd = ipv4.ipv4(proto = 1, src = dst_ip, dst = src_ip)
#icmp源和目的互换
icmp_hd = icmp.icmp(type_=icmp.ICMP_ECHO_REPLY,code=icmp.ICMP_ECHO_REPLY_CODE,csum=0,data=icmp_pkt.data)
icmp_reply = packet.Packet()
icmp_reply.add_protocol(ether_hd)
icmp_reply.add_protocol(ipv4_hd)
icmp_reply.add_protocol(icmp_hd)
icmp_reply.serialize()
#从inport把包发出去
actions = [parser.OFPActionOutput(in_port)];
out = parser.OFPPacketOut(datapath, ofproto.OFP_NO_BUFFER, ofproto.OFPP_CONTROLLER, actions, icmp_reply.data)
print("************* sending icmp packet out ***************")
datapath.send_msg(out)
可能不太完善,但能用
将在本地写好的controller文件scp到controller上
scp /Users/yujin/Desktop/sw2_h5_ctrl.py root@155.98.36.38:/usr/local/lib/python3.6/dist-packages/ryu/app
把controller文件运行起来
#在/usr/local/lib/python3.6/dist-packages/ryu/app路径下运行:
ryu-manager sw2_h5_ctrl.py --verbose
如果遇到这个问题:ImportError: cannot import name 'ALREADY_HANDLED':
需要在controller上执行:
pip3 install eventlet==0.30.2
然后再重新运行ryu-manager命令,亲测有效
此时,controller已经成功启动
(5)登陆两个switch节点,将switch1和switch2连入controller:
在switch1上执行:
ovs-vsctl set-controller br1 tcp:155.98.36.38:6633
在switch2上执行:
ovs-vsctl set-controller br2 tcp:155.98.36.38:6633
此时,网络已经全部搭建完毕
(6)登陆三个node节点
在node123上同时开启tcpdump对每条link上的packet进行监听,以观察packet是否从这个link上被转发
-i : tcpdump的参数,用于指定网卡,下面的命令中的网卡需要根据实际情况进行修改
#node1:
tcpdump -i enp4s0f0
#node2:
tcpdump -i enp6s0f1
#node3
tcpdump -i enp6s0f1
(7)登陆client
开多个client的shell窗口,分别在多个窗口执行rtmpdump:
#client窗口1里执行
rtmpdump -r rtmp://10.10.8.1:1935/play/3mins.mp4
#client窗口2里执行
rtmpdump -r rtmp://10.10.8.1:1935/play/3mins.mp4
此时观察node123上dump的包,发现它们转发了tcp_src不同的packet
到这里,实验就结束了,可以写博客总结一下了
最后
以上就是糟糕万宝路为你收集整理的ovs视频流转发实验的全部内容,希望文章能够帮你解决ovs视频流转发实验所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复