热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

RYU控制器的学习笔记(一)ryu.app.rest_router的分析

参考链接:https:www.cnblogs.comgoldsunshinep11720310.html1.默认的流表当你启动任何一个ryuapp之后࿰

参考链接:https://www.cnblogs.com/goldsunshine/p/11720310.html


1.默认的流表

当你启动任何一个ryu app之后,交换机的流表就被设置为默认的下面的内容

root@user-NF8480M5:/home/user/qyq/dragonflow/main# ovs-ofctl dump-flows ckltest-br
NXST_FLOW reply (xid=0x4):COOKIE=0x0, duration=1.737s, table=0, n_packets=0, n_bytes=0, idle_age=1, priority=1,arp actions=CONTROLLER:65535COOKIE=0x0, duration=1.737s, table=0, n_packets=0, n_bytes=0, idle_age=1, priority=1,ip actions=dropCOOKIE=0x0, duration=1.737s, table=0, n_packets=0, n_bytes=0, idle_age=1, priority=0 actions=NORMAL

2.默认流表下的流量转发

以同一个ovs上的主机Aping主机B为例,默认的话是ping不通的,仅仅增加arp表项的处理包个数

例如,下面的是192.168.1.2 ping 192.168.0.3,会首先寻找默认网关的mac地址

root@user-NF8480M5:/home/user# tcpdump -i tapq
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on tapq, link-type EN10MB (Ethernet), capture size 262144 bytes
16:29:36.922760 ARP, Request who-has 192.168.1.1 tell 192.168.1.2, length 28
16:29:37.939530 ARP, Request who-has 192.168.1.1 tell 192.168.1.2, length 28
16:29:38.963532 ARP, Request who-has 192.168.1.1 tell 192.168.1.2, length 28
16:29:39.987755 ARP, Request who-has 192.168.1.1 tell 192.168.1.2, length 28
16:29:41.011541 ARP, Request who-has 192.168.1.1 tell 192.168.1.2, length 28
16:29:42.035539 ARP, Request who-has 192.168.1.1 tell 192.168.1.2, length 28

刚好被流表接受到6个流量包

NXST_FLOW reply (xid=0x4):COOKIE=0x0, duration=574.422s, table=0, n_packets=6, n_bytes=252, idle_age=560, priority=1,arp actions=CONTROLLER:65535COOKIE=0x0, duration=574.422s, table=0, n_packets=0, n_bytes=0, idle_age=574, priority=1,ip actions=dropCOOKIE=0x0, duration=574.422s, table=0, n_packets=0, n_bytes=0, idle_age=574, priority=0 actions=NORMAL

3.CONTROLLER:65535是什么意思?

是指将流量包转发给控制器的意思


4.192.168.1.2 ping 192.168.0.3的提交给控制器的流量包的分析

第一个包(arp表项中有网关的地址)

packet_in_handler!, header_list is
{
'ethernet':
ethernet(dst='fa:c0:29:b8:c6:77',
ethertype=2048,src='86:3c:7c:4b:f1:7a'),
'icmp': icmp(code=0,csum=31873,
data=echo(data='\xce,\xc6^\x00\x00\x00\x00\x05
\x0f\x0f\x00\x00\x00\x00\x00\x10\x11\x12\x13\x14
\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f!"
#$%&\'()*+,-./01234567',id=5136,seq=1),type=8),
'ipv4':
ipv4(csum=13132,dst='192.168.0.3',flags=2,
header_length=5,identification=34055,offset=0,
option=None,proto=1,src='192.168.1.2',tos=0,
total_length=84,ttl=64,version=4)
}

dst_mac是网关的mac地址,

ipv4的源ip是192.168.1.2,目的ip是192.168.0.3

可以看一下网卡的mac

root@user-NF8480M5:/home/user/qyq/dragonflow/main# ip netn exec 82341 ip a
1: lo: mtu 65536 qdisc noqueue state UNKNOWN group default
qlen 1000link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00inet 127.0.0.1/8 scope host lovalid_lft forever preferred_lft forever
1040: vtapq@if1041: mtu 1500 qdisc noqueue state
UP group default qlen 1000link/ether 86:3c:7c:4b:f1:7a brd ff:ff:ff:ff:ff:ff link-netnsid 0inet 192.168.1.2/24 scope global vtapqvalid_lft forever preferred_lft forever
root@user-NF8480M5:/home/user/qyq/dragonflow/main# ip netn exec 82341 arp -a
? (192.168.1.1) 位于 fa:c0:29:b8:c6:77 [ether] 在 vtapq

下面是第一个包的发送过程,192.168.1.2 发送到的192.168.0.3的icmp包,但是mac地址是网关的mac地址

1, packet_in_handler
dp_id is 38074417935428
Router, msg.data
packet_in_handler!, header_list is {'ethernet': ethernet(dst='fa:c0:29:b8:c6:77',ethertype=2048,
src='86:3c:7c:4b:f1:7a'), 'icmp': icmp(code=0,csum=3622,
data=echo(data='\x8dC\xc6^\x00\x00\x00\x00t\x9b\x06
\x00\x00\x00\x00\x00\x10\x11\x12\x13\x14\x15\x16\x17
\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f !"
#$%&\'()*+,-./01234567',id=23752,seq=1),type=8),
'ipv4':ipv4(csum=12866,dst='192.168.0.3',
flags=2,header_length=5,identification=34321,
offset=0,option=None,proto=1,
src='192.168.1.2',tos=0,total_length=84,ttl=64,version=4)}
IPV4 in header_list
[RT][INFO] switch_id=000022a0e427f444:
Receive IP packet from [192.168.1.2] to an internal host [192.168.0.3].send_arp_request, src_ip is 192.168.0.1, dst_ip is 192.168.0.3[RT][INFO] switch_id=000022a0e427f444: Send ARP request (flood)

注意看倒数第二行,这个时候192.168.0.1(OVS网桥)发送了ARP报文,这个也可以从抓包分析里面得到结果。。

下面对192.168.0.3的网卡进行抓包

17:25:23.233879 ARP, Request who-has 192.168.0.1 tell 192.168.0.1, length 46
(初始化的时候)
17:25:23.241732 ARP, Request who-has 192.168.1.1 tell 192.168.1.1, length 46
(初始化的时候)
17:25:41.149465 ARP, Request who-has 192.168.0.3 tell 192.168.0.1, length 46
17:25:41.149489 ARP, Reply 192.168.0.3 is-at ca:4b:f7:92:f2:ef (oui Unknown), length 28
(第一次ping的时候,OVS需要知道192.168.0.3的MAC地址)
17:25:41.153425 IP 192.168.1.2 > 192.168.0.3: ICMP echo request, id 28032, seq 1, length 64
17:25:41.153453 IP 192.168.0.3 > 192.168.1.2: ICMP echo reply, id 28032, seq 1, length 64
(走下面的流表中被我标注的规则)
17:25:42.144964 IP 192.168.1.2 > 192.168.0.3: ICMP echo request, id 28032, seq 2, length 64
17:25:42.145003 IP 192.168.0.3 > 192.168.1.2: ICMP echo reply, id 28032, seq 2, length 64

下面是流表中的规则,其中我做记号的几行是被实际流量包走的规则

COOKIE=0x1, duration=418.888s, table=0, n_packets=0, n_bytes=0,idle_age=418, priority=1037,ip,nw_dst=192.168.0.1 actions=CONTROLLER:65535
COOKIE=0x2, duration=418.880s, table=0, n_packets=0, n_bytes=0,
idle_age=418, priority=1037,ip,nw_dst=192.168.1.1 actions=CONTROLLER:65535# 192.168.1.2 ping 192.168.0.3是直接走的下面这两行
COOKIE=0x2, duration=400.960s, table=0, n_packets=12, n_bytes=1176,
idle_timeout=1800, idle_age=2, priority=35,ip,
nw_dst=192.168.1.2 actions=dec_ttl,mod_dl_src:fa:c0:29:b8:c6:77,
mod_dl_dst:86:3c:7c:4b:f1:7a,output:1029COOKIE=0x1, duration=0.402s, table=0, n_packets=12, n_bytes=1176,
idle_timeout=1800, idle_age=2, priority=35,ip,
nw_dst=192.168.0.3 actions=dec_ttl,mod_dl_src:46:0e:bf:4a:7c:11,
mod_dl_dst:ca:4b:f7:92:f2:ef,output:1028COOKIE=0x1, duration=418.888s, table=0, n_packets=0, n_bytes=0,
idle_age=418, priority=36,ip,nw_src=192.168.0.0/24,nw_dst=192.168.0.0/24
actions=NORMAL
COOKIE=0x2, duration=418.880s, table=0, n_packets=0, n_bytes=0,
idle_age=418, priority=36,ip,nw_src=192.168.1.0/24,nw_dst=192.168.1.0/24
actions=NORMAL
COOKIE=0x1, duration=418.888s, table=0, n_packets=1, n_bytes=98,
idle_age=400, priority=2,ip,nw_dst=192.168.0.0/24 actions=CONTROLLER:65535
COOKIE=0x2, duration=418.880s, table=0, n_packets=1, n_bytes=98,
idle_age=400, priority=2,ip,nw_dst=192.168.1.0/24 actions=CONTROLLER:65535# arp走的是下面的这行规则
COOKIE=0x0, duration=495.466s, table=0, n_packets=47, n_bytes=1974,
idle_age=0, priority=1,arp actions=CONTROLLER:65535COOKIE=0x0, duration=495.466s, table=0, n_packets=9, n_bytes=756,
idle_age=4666, priority=1,ip actions=drop
COOKIE=0x0, duration=495.466s, table=0, n_packets=12, n_bytes=1062,
idle_age=2666, priority=0 actions=NORMAL

下面是第二个到达OVS的包,是192.168.0.3向192.168.0.1的arp响应报文

packet_in_handler!, header_list is {'arp': arp(dst_ip='192.168.0.1',dst_mac='46:0e:bf:4a:7c:11',
hlen=6,hwtype=1,opcode=2,plen=4,proto=2048,src_ip='192.168.0.3',
src_mac='ca:4b:f7:92:f2:ef'),
'ethernet': ethernet(dst='46:0e:bf:4a:7c:11',ethertype=2054,src='ca:4b:f7:92:f2:ef')
}

这个包是上一次缓存了ICMP包,并向所有网口洪泛了的ARP包,当192.168.0.3接收到ARP请求报文时,会发送ARP响应包给OVS

def _packetin_to_node(self, msg, header_list):if len(self.packet_buffer) >= MAX_SUSPENDPACKETS:self.logger.info('Packet is dropped, MAX_SUSPENDPACKETS exceeded.',extra=self.sw_id)return# Send ARP request to get node MAC address.in_port = self.ofctl.get_packetin_inport(msg)src_ip = Nonedst_ip = header_list[IPV4].dstsrcip = ip_addr_ntoa(header_list[IPV4].src)dstip = ip_addr_ntoa(dst_ip)address = self.address_data.get_data(ip=dst_ip)if address is not None:log_msg = 'Receive IP packet from [%s] to an internal host [%s].'self.logger.info(log_msg, srcip, dstip, extra=self.sw_id)src_ip = address.default_gw # 192.168.1.2, 192.168.1.1else:route = self.routing_tbl.get_data(dst_ip=dst_ip)if route is not None:log_msg = 'Receive IP packet from [%s] to [%s].'self.logger.info(log_msg, srcip, dstip, extra=self.sw_id)gw_address = self.address_data.get_data(ip=route.gateway_ip)if gw_address is not None:src_ip = gw_address.default_gwdst_ip = route.gateway_ipif src_ip is not None:self.packet_buffer.add(in_port, header_list, msg.data)self.send_arp_request(src_ip, dst_ip, in_port=in_port) // 这一行是重点self.logger.info('Send ARP request (flood)', extra=self.sw_id)

下面是第二个到达OVS的包,是192.168.0.3向192.168.0.1的arp响应报文

packet_in_handler!, header_list is {'arp': arp(dst_ip='192.168.0.1',dst_mac='46:0e:bf:4a:7c:11',
hlen=6,hwtype=1,opcode=2,plen=4,proto=2048,src_ip='192.168.0.3',
src_mac='ca:4b:f7:92:f2:ef'),
'ethernet': ethernet(dst='46:0e:bf:4a:7c:11',ethertype=2054,src='ca:4b:f7:92:f2:ef')
}

处理程序是自己写的

def packet_in_handler(self, msg, header_list):# Check invalid TTL (for OpenFlow V1.2/1.3)ofproto = self.dp.ofprotoif ofproto.OFP_VERSION == ofproto_v1_2.OFP_VERSION or \ofproto.OFP_VERSION == ofproto_v1_3.OFP_VERSION:if msg.reason == ofproto.OFPR_INVALID_TTL:self._packetin_invalid_ttl(msg, header_list)return# Analyze event type.if ARP in header_list:self._packetin_arp(msg, header_list)return.............

self._packetin_arp

def _packetin_arp(self, msg, header_list):src_addr = self.address_data.get_data(ip=header_list[ARP].src_ip)if src_addr is None:return# case: Receive ARP from the gateway# Update routing table.# case: Receive ARP from an internal host# Learning host MAC.gw_flg = self._update_routing_tbl(msg, header_list)if gw_flg is False:self._learning_host_mac(msg, header_list)..........

应该是receive ARP from an inernal host 

def _learning_host_mac(self, msg, header_list):# Set flow: routing to internal Host.out_port = self.ofctl.get_packetin_inport(msg)src_mac = header_list[ARP].src_macdst_mac = self.port_data[out_port].macsrc_ip = header_list[ARP].src_ipgateways = self.routing_tbl.get_gateways()if src_ip not in gateways:address = self.address_data.get_data(ip=src_ip)if address is not None:COOKIE = self._id_to_COOKIE(REST_ADDRESSID, address.address_id)priority = self._get_priority(PRIORITY_IMPLICIT_ROUTING)self.ofctl.set_routing_flow(COOKIE, priority,out_port, dl_vlan=self.vlan_id,src_mac=dst_mac, dst_mac=src_mac,nw_dst=src_ip,idle_timeout=IDLE_TIMEOUT,dec_ttl=True)self.logger.info('Set implicit routing flow [COOKIE=0x%x]',COOKIE, extra=self.sw_id)

下面是第三个到达OVS的包,是192.168.0.3到192.168.1.2的icmp echo response报文

packet_in_handler!,
header_list is
{'ethernet': ethernet(dst='46:0e:bf:4a:7c:11',ethertype=2048,src='ca:4b:f7:92:f2:ef'), 'icmp':
icmp(code=0,csum=16750,
data=echo(data='\xdbg\xc7^\x00\x00\x00\x00\xb7\x07
\x0f\x00\x00\x00\x00\x00\x10\x11\x12\x13\x14\x15
\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f !"#$%&\'()*+,
-./01234567',id=38639,seq=1),type=0),
'ipv4':
ipv4(csum=12313,dst='192.168.1.2',
flags=0,
header_length=5,identification=51258,
offset=0,option=None,
proto=1,
src='192.168.0.3',
tos=0,total_length=84,ttl=64,version=4)}

这个包的处理程序也是自己写的

下面第四个到达OVS的包,是192.168.1.2向192.168.1.1的arp响应报文

packet_in_handler!, header_list is
{
'arp':arp(dst_ip='192.168.1.1',dst_mac='fa:c0:29:b8:c6:77',
hlen=6,hwtype=1,opcode=2,plen=4,proto=2048,
src_ip='192.168.1.2',src_mac='86:3c:7c:4b:f1:7a'),
'ethernet':
ethernet(dst='fa:c0:29:b8:c6:77',
ethertype=2054,
src='86:3c:7c:4b:f1:7a')
}

5.新增加的规则分析

COOKIE=0x3, duration=1.625s, table=0, n_packets=1,
n_bytes=98, idle_timeout=1800, idle_age=6,
priority=35,ip,nw_dst=192.168.0.3 actions=dec_ttl, // ttl减少一个,为0的时候丢弃
mod_dl_src:46:0e:bf:4a:7c:11, // tapq1的veth的mac
mod_dl_dst:ca:4b:f7:92:f2:ef, // vtapq1的veth的mac
output:1028 // tapq1COOKIE=0x4, duration=1.624s, table=0, n_packets=1,
n_bytes=98, idle_timeout=1800,
idle_age=6,
priority=35,ip,nw_dst=192.168.1.2
actions=dec_ttl,
mod_dl_src:fa:c0:29:b8:c6:77,
mod_dl_dst:86:3c:7c:4b:f1:7a,output:1029

这是为什么呢,由于我们是1.2 ping 0.3,先看1.2的mac地址

1040: vtapq@if1041: mtu 1500
qdisc noqueue state UP group default qlen 1000
link/ether 86:3c:7c:4b:f1:7a brd ff:ff:ff:ff:ff:ff link-netnsid 0
inet 192.168.1.2/24 scope global vtapq
valid_lft forever preferred_lft forever

1.2的mac地址是86:3c:7c:4b:f1:7a

0.3的mac地址

1038: vtapq1@if1039:
mtu 1500 qdisc noqueue state UP group default qlen 1000
link/ether ca:4b:f7:92:f2:ef brd ff:ff:ff:ff:ff:ff link-netnsid 0
inet 192.168.0.3/24 scope global vtapq1
valid_lft forever preferred_lft forever

看一看他们的veth设备的mac地址

1039: tapq1@if1038: mtu 1500 qdisc noqueue master ovs-system state UP group default qlen 1000link/ether 46:0e:bf:4a:7c:11 brd ff:ff:ff:ff:ff:ff link-netnsid 2inet6 fe80::440e:bfff:fe4a:7c11/64 scope link valid_lft forever preferred_lft forever
1041: tapq@if1040:
mtu 1500 qdisc noqueue master ovs-system state UP group default qlen 1000link/ether fa:c0:29:b8:c6:77 brd ff:ff:ff:ff:ff:ff link-netnsid 3inet6 fe80::f8c0:29ff:feb8:c677/64 scope link valid_lft forever preferred_lft forever

看看端口

# ovs-ofctl show ckltest-br
OFPT_FEATURES_REPLY (xid=0x2): dpid:000022a0e427f444
n_tables:254, n_buffers:256
capabilities: FLOW_STATS TABLE_STATS PORT_STATS QUEUE_STATS ARP_MATCH_IP
actions: output enqueue set_vlan_vid set_vlan_pcp
strip_vlan mod_dl_src mod_dl_dst mod_nw_src mod_nw_dst
mod_nw_tos mod_tp_src mod_tp_dst1028(tapq1): addr:46:0e:bf:4a:7c:11config: 0state: 0current: 10GB-FD COPPERspeed: 10000 Mbps now, 0 Mbps max1029(tapq): addr:fa:c0:29:b8:c6:77config: 0state: 0current: 10GB-FD COPPERspeed: 10000 Mbps now, 0 Mbps maxLOCAL(ckltest-br): addr:22:a0:e4:27:f4:44config: 0state: 0speed: 0 Mbps now, 0 Mbps max
OFPT_GET_CONFIG_REPLY (xid=0x4): frags=normal miss_send_len=0

6.192.168.1.2对应的tapq上实际捕获的流量分析
ICMP ECHO REQUEST

ICMP ECHO REPLY


7.192.168.0.3对应的tapq1上面实际捕获的流量的分析

ICMP ECHO REQUEST

ICMP ECHO REPLY

8.TTL分析

192.168.1.2 ping 192.168.0.3 第一次到达tapq的时候,如下

然后经过OVS之后,TTL-1 ,MAC地址被修改并转发给tapq1,如下

这样就实现了转发


8.ping默认网关

192.168.0.3 ping 192.168.0.1

代码如下

if IPV4 in header_list:rt_ports = self.address_data.get_default_gw() # 这个是网关的ipif header_list[IPV4].dst in rt_ports:# Packet to router's port.if ICMP in header_list:if header_list[ICMP].type == icmp.ICMP_ECHO_REQUEST:self._packetin_icmp_req(msg, header_list)returnelif TCP in header_list or UDP in header_list:self._packetin_tcp_udp(msg, header_list)returnelse:# Packet to internal host or gateway router.self._packetin_to_node(msg, header_list)return

self._packetin_icmp_req:发送ICMP_ECHO_REPLY

def _packetin_icmp_req(self, msg, header_list):# Send ICMP echo reply.in_port = self.ofctl.get_packetin_inport(msg)self.ofctl.send_icmp(in_port, header_list, self.vlan_id,icmp.ICMP_ECHO_REPLY,icmp.ICMP_ECHO_REPLY_CODE,icmp_data=header_list[ICMP].data)srcip = ip_addr_ntoa(header_list[IPV4].src)dstip = ip_addr_ntoa(header_list[IPV4].dst)log_msg = 'Receive ICMP echo request from [%s] to router port [%s].'self.logger.info(log_msg, srcip, dstip, extra=self.sw_id)self.logger.info('Send ICMP echo reply to [%s].', srcip,extra=self.sw_id)

self.packetin_tcp_udp:

响应ICMP_PORT_UNREACH_ERROR

def _packetin_tcp_udp(self, msg, header_list):# Send ICMP port unreach error.in_port = self.ofctl.get_packetin_inport(msg)self.ofctl.send_icmp(in_port, header_list, self.vlan_id,icmp.ICMP_DEST_UNREACH,icmp.ICMP_PORT_UNREACH_CODE,msg_data=msg.data)srcip = ip_addr_ntoa(header_list[IPV4].src)dstip = ip_addr_ntoa(header_list[IPV4].dst)self.logger.info('Receive TCP/UDP from [%s] to router port [%s].',srcip, dstip, extra=self.sw_id)self.logger.info('Send ICMP destination unreachable to [%s].', srcip,extra=self.sw_id)

9.日志分析

第一个icmp包

[RT][INFO] switch_id=000022a0e427f444: Receive IP packet
from [192.168.1.2] to an internal host [192.168.0.3].
[RT][INFO] switch_id=000022a0e427f444: Send ARP request (flood)

收到icmp包,向所有端口洪泛arp

第一个arp包

[RT][INFO] switch_id=000022a0e427f444: Set implicit routing flow [COOKIE=0x1]
[RT][INFO] switch_id=000022a0e427f444: Receive ARP reply from [192.168.0.3]to router port [192.168.0.1].
[RT][INFO] switch_id=000022a0e427f444: Send suspend packet to [192.168.0.3].

收到arp包,直接设置点对点的转发规则,然后将上一个icmp包发送到192.168.0.3

返程的报文分析同理

ping一个网关

[RT][INFO] switch_id=000022a0e427f444: Receive ICMP echo request from [192.168.0.3]
to router port [192.168.0.1].
[RT][INFO] switch_id=000022a0e427f444: Send ICMP echo reply to [192.168.0.3].

10.从流量中观察流表下发过程

 

8,9,10号包?

转发所有的arp报文

其中有个send_arp函数

def send_arp_request(self, src_ip, dst_ip, in_port=None):# Send ARP request from all ports.for send_port in self.port_data.values():if in_port is None or in_port != send_port.port_no:src_mac = send_port.macdst_mac = mac_lib.BROADCAST_STRarp_target_mac = mac_lib.DONTCARE_STRinport = self.ofctl.dp.ofproto.OFPP_CONTROLLERoutput = send_port.port_noself.ofctl.send_arp(arp.ARP_REQUEST, self.vlan_id,src_mac, dst_mac, src_ip, dst_ip,arp_target_mac, inport, output)

由于连接着OVS的都是veth设备,所以相当于每个veth设备都向自己的对端发送了arp报文,然后如果对端是对应的ip地址的话,对端会返回arp报文。


推荐阅读
  • 欢乐的票圈重构之旅——RecyclerView的头尾布局增加
    项目重构的Git地址:https:github.comrazerdpFriendCircletreemain-dev项目同步更新的文集:http:www.jianshu.comno ... [详细]
  • CF:3D City Model(小思维)问题解析和代码实现
    本文通过解析CF:3D City Model问题,介绍了问题的背景和要求,并给出了相应的代码实现。该问题涉及到在一个矩形的网格上建造城市的情景,每个网格单元可以作为建筑的基础,建筑由多个立方体叠加而成。文章详细讲解了问题的解决思路,并给出了相应的代码实现供读者参考。 ... [详细]
  • 推荐系统遇上深度学习(十七)详解推荐系统中的常用评测指标
    原创:石晓文小小挖掘机2018-06-18笔者是一个痴迷于挖掘数据中的价值的学习人,希望在平日的工作学习中,挖掘数据的价值, ... [详细]
  • 本文介绍了响应式页面的概念和实现方式,包括针对不同终端制作特定页面和制作一个页面适应不同终端的显示。分析了两种实现方式的优缺点,提出了选择方案的建议。同时,对于响应式页面的需求和背景进行了讨论,解释了为什么需要响应式页面。 ... [详细]
  • Nginx使用(server参数配置)
    本文介绍了Nginx的使用,重点讲解了server参数配置,包括端口号、主机名、根目录等内容。同时,还介绍了Nginx的反向代理功能。 ... [详细]
  • Java实战之电影在线观看系统的实现
    本文介绍了Java实战之电影在线观看系统的实现过程。首先对项目进行了简述,然后展示了系统的效果图。接着介绍了系统的核心代码,包括后台用户管理控制器、电影管理控制器和前台电影控制器。最后对项目的环境配置和使用的技术进行了说明,包括JSP、Spring、SpringMVC、MyBatis、html、css、JavaScript、JQuery、Ajax、layui和maven等。 ... [详细]
  • 本文介绍了如何找到并终止在8080端口上运行的进程的方法,通过使用终端命令lsof -i :8080可以获取在该端口上运行的所有进程的输出,并使用kill命令终止指定进程的运行。 ... [详细]
  • 生成对抗式网络GAN及其衍生CGAN、DCGAN、WGAN、LSGAN、BEGAN介绍
    一、GAN原理介绍学习GAN的第一篇论文当然由是IanGoodfellow于2014年发表的GenerativeAdversarialNetworks(论文下载链接arxiv:[h ... [详细]
  • javascript  – 概述在Firefox上无法正常工作
    我试图提出一些自定义大纲,以达到一些Web可访问性建议.但我不能用Firefox制作.这就是它在Chrome上的外观:而那个图标实际上是一个锚点.在Firefox上,它只概述了整个 ... [详细]
  • ZSI.generate.Wsdl2PythonError: unsupported local simpleType restriction ... [详细]
  • 本文介绍了Perl的测试框架Test::Base,它是一个数据驱动的测试框架,可以自动进行单元测试,省去手工编写测试程序的麻烦。与Test::More完全兼容,使用方法简单。以plural函数为例,展示了Test::Base的使用方法。 ... [详细]
  • Google Play推出全新的应用内评价API,帮助开发者获取更多优质用户反馈。用户每天在Google Play上发表数百万条评论,这有助于开发者了解用户喜好和改进需求。开发者可以选择在适当的时间请求用户撰写评论,以获得全面而有用的反馈。全新应用内评价功能让用户无需返回应用详情页面即可发表评论,提升用户体验。 ... [详细]
  • Python正则表达式学习记录及常用方法
    本文记录了学习Python正则表达式的过程,介绍了re模块的常用方法re.search,并解释了rawstring的作用。正则表达式是一种方便检查字符串匹配模式的工具,通过本文的学习可以掌握Python中使用正则表达式的基本方法。 ... [详细]
  • Webmin远程命令执行漏洞复现及防护方法
    本文介绍了Webmin远程命令执行漏洞CVE-2019-15107的漏洞详情和复现方法,同时提供了防护方法。漏洞存在于Webmin的找回密码页面中,攻击者无需权限即可注入命令并执行任意系统命令。文章还提供了相关参考链接和搭建靶场的步骤。此外,还指出了参考链接中的数据包不准确的问题,并解释了漏洞触发的条件。最后,给出了防护方法以避免受到该漏洞的攻击。 ... [详细]
  • 拥抱Android Design Support Library新变化(导航视图、悬浮ActionBar)
    转载请注明明桑AndroidAndroid5.0Loollipop作为Android最重要的版本之一,为我们带来了全新的界面风格和设计语言。看起来很受欢迎࿰ ... [详细]
author-avatar
路边一烧饼
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有