文章目录

  • 无端ARP
    • L2地址发生了变化
    • L3地址重复检测
    • 虚拟IP
  • ARP选项
    • arp_announce
    • arp_ignore
    • arp_filter
    • arp_accept
  • ARP报文格式
  • 发送ARP请求: arp_solicit()
    • 构造&&发送ARP报文: arp_send()
      • 构造ARP报文: arp_create()
      • 发送ARP报文: arp_xmit()
  • 接收ARP报文: arp_rcv()
    • 处理ARP报文: arp_process()
      • ARP请求忽略识别: arp_ignore()
      • ARP请求过滤: arp_filter()
      • 被动学习: neigh_event_ns()

无端ARP

大多数情况下,主机发送ARP请求都是为了解析L3地址对应的L2地址,但是特殊情况下,也可以用ARP请求来通知接收方自己发生了一些事情,这种ARP请求也称为无端ARP。常见有下面三种情况。

L2地址发生了变化

默认情况下,如果一个主机L2地址发生了变化,同一个网络中的其它主句并不能立刻感知到,它们只能通过邻居子系统的过期机制探测,这会导致有一小段时间数据包是不可达的。如果愿意,发生这种情况是,本地主机主动发送一个ARP请求,触发接收方刷新缓存。

要注意的是内核并不会在L2地址变化时执行这个操作,如果需要,应该由用户态触发。

L3地址重复检测

当主机通过静态配置一个L3地址后,可以通过发送一个目的地址为配置的L3地址的ARP请求来识别同网段中是否有其它主机使用该地址,如果收到了应答,说明有人再使用,否则没有。

这只是提供了一种L3地址重复检测的方式,但是不太介意使用这种方式,DHCP方式获取IP地址才是正途。

虚拟IP

在服务器备份设计中,如果服务器A出现了问题,启动备份服务器B后,让B继承A的IP地址,然后B向网络中发送一个ARP请求,触发网络中其它主机更新对该IP地址的缓存,可以实现快速的切换。

这种场景的思想和L2地址发生变化的场景是一致的,都是为了让其它主机能够及时刷新自己的邻居项缓存。

ARP选项

在IPv4配置块中,有几个选项会影响ARP协议的行为,和IPv4配置块中的其它配置一样,这些选项可以是网络设备级别的配置,也可以是全局配置。

arp_announce

网络设备可以配置多个IP地址,当发送ARP请求时,到底应该选择使用哪个IP地址作为源地址封装到ARP请求报文中,该选项用于控制这种选择行为,可取三个值:

  • 0:任何本地IP地址都可以;
  • 1:如果可能,选择和目的地址位于同一个子网内的地址,否则按照配置2选择;
  • 2:优先使用主地址;

arp_ignore

由于IP地址属于主机,但是是配置到网络设备上的,所以主机有可能在网络设备A上收到一个对网络设备B上的一个地址的查询(如这两个设备都有属于同一个子网的IP地址配置)。主机是否应该对这种ARP请求做出应答是可配置的,这种行为就是通过该选项控制。该选项可取的值以及它们的含义见下面的arp_ingore()函数。

arp_filter

当主机有两个网络设备,它们连接到同一个局域网,并且配置了同一个子网的IP地址时,对于子网内其它主机的的ARP请求,这两个网络设备都可以收到,该选项用于确定由哪个网络设备来对该请求做出响应。

arp_accept

如果本机没有发送ARP请求,但是收到了一个ARP响应,ARP协议使用该选项来决定是否使用该ARP响应中的源IP和源L2地址映射关系。

和其它选项不同,ARP使用的是IP配置块中all中的配置值,并非网络设备上的配置值。

ARP报文格式

struct arphdr
{__be16 ar_hrd; // L2层帧类型,取值ARPHRD_ETHER等__be16 ar_pro; // L3层地址类型,取值如ETH_P_IPunsigned char ar_hln; // L2层地址长度unsigned char ar_pln; // L3层地址长度__be16 ar_op; // ARP操作码,如ARPOP_REQUEST等// 下面的内容根据L2和L3地址长度不同有所不同,但是实现上,L3地址实际上就是IP地址(很多地方代码写死了)
#if 0/**     Ethernet looks like this : This bit is variable sized however...*/unsigned char        ar_sha[ETH_ALEN];   /* sender hardware address  */unsigned char     ar_sip[4];      /* sender IP address        */unsigned char     ar_tha[ETH_ALEN];   /* target hardware address  */unsigned char     ar_tip[4];      /* target IP address        */
#endif
};

ARP协议报文的前8字节是固定的,后面部分根据协议地址长度以及L2层地址长度不同有所变化,可见在设计的时候,ARP不仅仅是为IPv4服务的,只不过实际中只有IPv4使用它而已。

发送ARP请求: arp_solicit()

在邻居子系统数据发送流程中有看到,ARP协议的邻居项操作集实现中。其中非常重要的一个接口就是Solicitation请求的发送,邻居子系统框架会在需要的时候回调该函数(在NUD_INCOMPLETE和NUD_PROBE状态)。

static void arp_solicit(struct neighbour *neigh, struct sk_buff *skb)
{__be32 saddr = 0;u8  *dst_ha = NULL;struct net_device *dev = neigh->dev;__be32 target = *(__be32*)neigh->primary_key; // 数据包的下一跳地址int probes = atomic_read(&neigh->probes);struct in_device *in_dev = in_dev_get(dev); // 找到该网络设备的IPv4配置块if (!in_dev)return;// 根据该网络设备的IPv4配置项arp_announce选择一个出口IP地址switch (IN_DEV_ARP_ANNOUNCE(in_dev)) {default:case 0:// 0配置为默认值,此时优先选择数据包的源地址if (skb && inet_addr_type(dev_net(dev), ip_hdr(skb)->saddr) == RTN_LOCAL)saddr = ip_hdr(skb)->saddr;break;case 1:// 1配置表示必须选择一个和出口数据包在同一个子网的源IP地址if (!skb)break;saddr = ip_hdr(skb)->saddr;if (inet_addr_type(dev_net(dev), saddr) == RTN_LOCAL) {// target、saddr和本地网络设备上的某个IP地址均属于同一个子网if (inet_addr_onlink(in_dev, target, saddr))break;}saddr = 0;break;case 2:/* Avoid secondary IPs, get a primary/preferred one */break;}// 可见,arp_announce配置仅仅是一种建议,如果都无法选出,则用inet_select_addr()选择一个if (in_dev)in_dev_put(in_dev);if (!saddr)// 优先选择一个和target属于同一个子网的主IP地址saddr = inet_select_addr(dev, target, RT_SCOPE_LINK);if ((probes -= neigh->parms->ucast_probes) < 0) {// 单播场景if (!(neigh->nud_state&NUD_VALID))printk(KERN_DEBUG "trying to ucast probe in NUD_INVALID\n");dst_ha = neigh->ha;read_lock_bh(&neigh->lock);} else if ((probes -= neigh->parms->app_probes) < 0) {#ifdef CONFIG_ARPD// 用户态的arp实现neigh_app_ns(neigh);
#endifreturn;}// 构造并发送ARP请求报文,如果dst_ha为NULL则发送ARP广播arp_send(ARPOP_REQUEST, ETH_P_ARP, target, dev, saddr,dst_ha, dev->dev_addr, NULL);if (dst_ha)read_unlock_bh(&neigh->lock);
}

arp_solicit()最重要的工作就是为ARP请求选择源IP地址、目的L2地址(单播或者广播)。

源IP地址的选择过程受ARP选项arp_nonounce的影响。选择策略如下:

  1. 如果arp_nonunce为0(默认值),若IP包头中的源IP地址就是本地地址,则选中它,否则按照步骤3选择;
  2. 如果arp_nonunce为1,若IP包头中的源IP地址就是本地地址,并且它和本地网络设备上的某个IP地址处于同一个子网,则选中它,否则按照步骤3选择;
  3. 使用inet_select_addr()选择一个IP地址,该函数会优先选择一个和输入地址(下一跳地址)位于同一个子网的地址,否则任意选择一个主地址;

构造&&发送ARP报文: arp_send()

/**  Create and send an arp packet.*/
void arp_send(int type, int ptype, __be32 dest_ip, struct net_device *dev, __be32 src_ip,const unsigned char *dest_hw, const unsigned char *src_hw, const unsigned char *target_hw)
{struct sk_buff *skb;/**    No arp on this interface.*/if (dev->flags&IFF_NOARP)return;// 构造ARP请求报文skb = arp_create(type, ptype, dest_ip, dev, src_ip,dest_hw, src_hw, target_hw);if (skb == NULL) {return;}// 发送ARP报文arp_xmit(skb);
}

构造ARP报文: arp_create()

/**  Create an arp packet. If (dest_hw == NULL), we create a broadcast*    message.*/
@type: ARP报文类型,如ARP_REQUEST;
@ptype: L2帧首部的协议字段;
@dest_ip: 下一跳IP地址;
@src_ip: 本机IP地址;
@dest_hw: 下一跳L2层地址,该地址会作为L2帧的目的地址,所以如果全0,会设置为广播
@src_hw: 本机L2层地址;
@target_hw: ARP报文中填充的目标主机L2层地址
struct sk_buff *arp_create(int type, int ptype, __be32 dest_ip, struct net_device *dev, __be32 src_ip,const unsigned char *dest_hw, const unsigned char *src_hw, const unsigned char *target_hw)
{struct sk_buff *skb;struct arphdr *arp;unsigned char *arp_ptr;/**  Allocate a buffer*/// arp_hdr_len()为标准ARP首部+2个IP地址+2个L2层地址长度skb = alloc_skb(arp_hdr_len(dev) + LL_ALLOCATED_SPACE(dev), GFP_ATOMIC);if (skb == NULL)return NULL;skb_reserve(skb, LL_RESERVED_SPACE(dev));skb_reset_network_header(skb);arp = (struct arphdr *) skb_put(skb, arp_hdr_len(dev));skb->dev = dev;skb->protocol = htons(ETH_P_ARP);if (src_hw == NULL) // 源地址src_hw = dev->dev_addr;if (dest_hw == NULL) // 如果不指定目的地址构造的就是广播报文dest_hw = dev->broadcast;// 调用网络设备提供的接口填充ARP报文的L2帧首部if (dev_hard_header(skb, dev, ptype, dest_hw, src_hw, skb->len) < 0)goto out;/** Fill out the arp protocol part.** The arp hardware type should match the device type, except for FDDI,* which (according to RFC 1390) should always equal 1 (Ethernet).*//** Exceptions everywhere. AX.25 uses the AX.25 PID value not the*  DIX code for the protocol. Make these device structure fields.*/// 根据不同的设备类型,填充标准ARP报文首部字段switch (dev->type) {default:arp->ar_hrd = htons(dev->type);arp->ar_pro = htons(ETH_P_IP);break;
...}arp->ar_hln = dev->addr_len;arp->ar_pln = 4;arp->ar_op = htons(type);arp_ptr=(unsigned char *)(arp+1);memcpy(arp_ptr, src_hw, dev->addr_len);arp_ptr += dev->addr_len;memcpy(arp_ptr, &src_ip, 4);arp_ptr += 4;if (target_hw != NULL)memcpy(arp_ptr, target_hw, dev->addr_len);elsememset(arp_ptr, 0, dev->addr_len);arp_ptr += dev->addr_len;memcpy(arp_ptr, &dest_ip, 4);return skb;
out:kfree_skb(skb);return NULL;
}

发送ARP报文: arp_xmit()

注意,经过过滤器后直接调用的是设备接口层的发送函数dev_queue_xmit(),不会再进入邻居子系统了。

/**  Send an arp packet.*/
void arp_xmit(struct sk_buff *skb)
{/* Send it off, maybe filter it using firewalling first.  */NF_HOOK(NFPROTO_ARP, NF_ARP_OUT, skb, NULL, skb->dev, dev_queue_xmit);
}

接收ARP报文: arp_rcv()

/**  Receive an arp request from the device layer.*/
static int arp_rcv(struct sk_buff *skb, struct net_device *dev,struct packet_type *pt, struct net_device *orig_dev)
{struct arphdr *arp;/* ARP header, plus 2 device addresses, plus 2 IP addresses.  */if (!pskb_may_pull(skb, arp_hdr_len(dev))) // 确保报文有足够的ARP报文首部goto freeskb;// 校验ARP报文首部,确保数据是发给本设备的arp = arp_hdr(skb);if (arp->ar_hln != dev->addr_len || dev->flags & IFF_NOARP ||skb->pkt_type == PACKET_OTHERHOST || skb->pkt_type == PACKET_LOOPBACK ||arp->ar_pln != 4)goto freeskb;if ((skb = skb_share_check(skb, GFP_ATOMIC)) == NULL)goto out_of_mem;// 设置skb控制块为0memset(NEIGH_CB(skb), 0, sizeof(struct neighbour_cb));// 经过过滤器后调用arp_process()return NF_HOOK(NFPROTO_ARP, NF_ARP_IN, skb, dev, NULL, arp_process);freeskb:kfree_skb(skb);
out_of_mem:return 0;
}

处理ARP报文: arp_process()

static int arp_process(struct sk_buff *skb)
{struct net_device *dev = skb->dev;struct in_device *in_dev = in_dev_get(dev);struct arphdr *arp;unsigned char *arp_ptr;struct rtable *rt;unsigned char *sha;__be32 sip, tip;u16 dev_type = dev->type;int addr_type;struct neighbour *n;struct net *net = dev_net(dev);/* arp_rcv below verifies the ARP header and verifies the device* is ARP'able.*/if (in_dev == NULL)goto out;arp = arp_hdr(skb);// 根据设备类型,再次校验ARP报文首部字段,我们只关注default分支,即以太网switch (dev_type) {default:if (arp->ar_pro != htons(ETH_P_IP) || htons(dev_type) != arp->ar_hrd)goto out;break;
...}// 只处理ARP请求和ARP应答报文if (arp->ar_op != htons(ARPOP_REPLY) && arp->ar_op != htons(ARPOP_REQUEST))goto out;/**  Extract fields*/arp_ptr= (unsigned char *)(arp+1); // 实际跳过的是sizeof(arphdr)字节sha   = arp_ptr; // 发送方L2层地址保存在sha中arp_ptr += dev->addr_len;memcpy(&sip, arp_ptr, 4); // 发送方IP地址保存在sip中arp_ptr += 4;arp_ptr += dev->addr_len;memcpy(&tip, arp_ptr, 4); // 目的方IP地址保存在tip中
/** Check for bad requests for 127.x.x.x and requests for multicast*    addresses.  If this is one such, delete it.*/// 异常的目的地址,多播地址是不需要ARP查询的if (ipv4_is_loopback(tip) || ipv4_is_multicast(tip))goto out;// 仔细阅读注释
/**  Process entry.  The idea here is we want to send a reply if it is a*  request for us or if it is a request for someone else that we hold*  a proxy for.  We want to add an entry to our cache if it is a reply*  to us or if it is a request for our address.*  (The assumption for this last is that if someone is requesting our*  address, they are probably intending to talk to us, so it saves time*  if we cache their address.  Their address is also probably not in*  our cache, since ours is not in their cache.)**  Putting this another way, we only care about replies if they are to*  us, in which case we add them to the cache.  For requests, we care*  about those for us and those for our proxies.  We reply to both,*  and in the case of requests for us we add the requester to the arp*  cache.*//* Special case: IPv4 duplicate address detection packet (RFC2131) */// 无端ARP中的IP地址重复检测场景,如果允许,回复ARP响应报文if (sip == 0) {if (arp->ar_op == htons(ARPOP_REQUEST) &&inet_addr_type(net, tip) == RTN_LOCAL &&!arp_ignore(in_dev, sip, tip))arp_send(ARPOP_REPLY, ETH_P_ARP, sip, dev, tip, sha, dev->dev_addr, sha);goto out;}// 对于ARP请求报文,路由查询可以过滤掉本地路由不应该接收的报文if (arp->ar_op == htons(ARPOP_REQUEST) &&ip_route_input(skb, tip, sip, 0, dev) == 0) {rt = skb->rtable;addr_type = rt->rt_type;if (addr_type == RTN_LOCAL) { // ARP请求来自本地子网int dont_send = 0;if (!dont_send)dont_send |= arp_ignore(in_dev,sip,tip); // 检查是否需要忽略该报文if (!dont_send && IN_DEV_ARPFILTER(in_dev)) // 如果开启ARP报文过滤,则进行过滤dont_send |= arp_filter(sip,tip,dev);if (!dont_send) {// ARP请求报文可以接收,作为接收方,也可以从中获取到发送方的地址映射关系,// 使用这个信息更新或者创建邻居项,相比于主动发送,这种方式也叫被动学习n = neigh_event_ns(&arp_tbl, sha, &sip, dev);// 实际上即使本地缓存失败,也应该向发送方回应ARP响应if (n) {arp_send(ARPOP_REPLY,ETH_P_ARP,sip,dev,tip,sha,dev->dev_addr,sha);neigh_release(n);}}goto out;} else if (IN_DEV_FORWARD(in_dev)) { // ARP报文不是给本机的,并且设备开启了转发能力,根据是否启用代理功能对数据包进行转发,// 这部分逻辑分析见“邻居子系统之代理功能”if (addr_type == RTN_UNICAST  && rt->u.dst.dev != dev &&(arp_fwd_proxy(in_dev, rt) || pneigh_lookup(&arp_tbl, net, &tip, dev, 0))) {n = neigh_event_ns(&arp_tbl, sha, &sip, dev);if (n)neigh_release(n);if (NEIGH_CB(skb)->flags & LOCALLY_ENQUEUED ||skb->pkt_type == PACKET_HOST ||in_dev->arp_parms->proxy_delay == 0) {arp_send(ARPOP_REPLY,ETH_P_ARP,sip,dev,tip,sha,dev->dev_addr,sha);} else {pneigh_enqueue(&arp_tbl, in_dev->arp_parms, skb);in_dev_put(in_dev);return 0;}goto out;}}}// ARP响应报文,或者是不该接收的ARP请求报文(路由查询失败),到这里更新本地邻居表/* Update our ARP tables */// 根据ARP报文的源IP地址查询邻居项,最后一个参数为0,查询失败不会创建邻居项n = __neigh_lookup(&arp_tbl, &sip, dev, 0);if (IPV4_DEVCONF_ALL(dev_net(dev), ARP_ACCEPT)) { // 判断的是all配置中的设置/* Unsolicited ARP is not accepted by default.It is possible, that this option should be enabled for somedevices (strip is candidate)*/// 下面情况成立,说明本机没有对应的ARP请求,但是收到了一个ARP响应,// 如果arp_accept选项打开,那么也用该信息更新邻居表if (n == NULL &&arp->ar_op == htons(ARPOP_REPLY) &&inet_addr_type(net, sip) == RTN_UNICAST)n = __neigh_lookup(&arp_tbl, &sip, dev, 1);}if (n) { // 更新邻居项状态int state = NUD_REACHABLE;int override;/* If several different ARP replies follows back-to-back,use the FIRST one. It is possible, if several proxyagents are active. Taking the first reply preventsarp trashing and chooses the fastest router.*/// 锁定期内只按照第一个更新override = time_after(jiffies, n->updated + n->parms->locktime);/* Broadcast replies and request packetsdo not assert neighbour reachability.*/// 默认是更新到NUD_REACHABLE,但是对于ARP请求和广播的ARP响应,仅仅更新到NUD_STALEif (arp->ar_op != htons(ARPOP_REPLY) || skb->pkt_type != PACKET_HOST)state = NUD_STALE;neigh_update(n, sha, state, override ? NEIGH_UPDATE_F_OVERRIDE : 0);neigh_release(n);}out:if (in_dev)in_dev_put(in_dev);kfree_skb(skb);return 0;
}

ARP请求忽略识别: arp_ignore()

该函数用于识别是否应该忽略收到的ARP请求报文。

// 调用者已经确保tip是本地地址
static int arp_ignore(struct in_device *in_dev, __be32 sip, __be32 tip)
{int scope;// 根据网络设备上的arp_ignore选项做不同处理switch (IN_DEV_ARP_IGNORE(in_dev)) {case 0:  // 任何本地地址的ARP请求都应该应答(默认值)return 0;case 1: // 目的地址必须是配置在输入接口上的地址才应答sip = 0;scope = RT_SCOPE_HOST;break;case 2:// 目的地址必须是配置在数据接口上,并且和sip必须是同一个子网才应答scope = RT_SCOPE_HOST;break;case 3:    /* Do not reply for scope host addresses */sip = 0;scope = RT_SCOPE_LINK;break;case 4:    /* Reserved */case 5:case 6:case 7:return 0;case 8:// 不应答return 1;default:return 0;}// 进行目的地址确认return !inet_confirm_addr(in_dev, sip, tip, scope);
}

ARP请求过滤: arp_filter()

该函数是对前面提到的arp_filter的实现。该函数会使得只有本机可以到达发送方,并且是出口设备收到ARP请求时才会处理ARP请求报文。

static int arp_filter(__be32 sip, __be32 tip, struct net_device *dev)
{struct flowi fl = { .nl_u = { .ip4_u = { .daddr = sip,.saddr = tip } } };struct rtable *rt;int flag = 0;/*unsigned long now; */struct net *net = dev_net(dev);if (ip_route_output_key(net, &rt, &fl) < 0)return 1;if (rt->u.dst.dev != dev) {NET_INC_STATS_BH(net, LINUX_MIB_ARPFILTER);flag = 1;}ip_rt_put(rt);return flag;
}

被动学习: neigh_event_ns()

struct neighbour *neigh_event_ns(struct neigh_table *tbl, u8 *lladdr, void *saddr,struct net_device *dev)
{// 先查询邻居表中是否有该邻居向,如果没有,该函数会新建一个(根据lladdr)struct neighbour *neigh = __neigh_lookup(tbl, saddr, dev, lladdr || !dev->addr_len);if (neigh)// 将邻居项状态更新未NUD_STALE,特别注意,如果原来是NUD_REACHABLE是不会将其变为NUD_STALE的neigh_update(neigh, lladdr, NUD_STALE, NEIGH_UPDATE_F_OVERRIDE);return neigh;
}

邻居子系统之ARP协议数据处理过程相关推荐

  1. Linux内核邻接子系统(arp协议)的工作原理

    主要参考了<深入linux内核架构>和<精通Linux内核网络>相关章节 文章目录 Linux内核邻接子系统(二层到三层) 邻接子系统的核心 struct neighbour ...

  2. 邻居子系统:地址解析协议

    2019独角兽企业重金招聘Python工程师标准>>>   无端ARP 一般来说,发出一个ARPOP_REQUEST是由于发送方想和一个IP地址通信,需要找到其对应的L2地址.但有时 ...

  3. Linux_网络_数据链路层协议 MAC帧/ARP协议 (以太网通信原理,MAC地址与IP地址的区分,MTU对IP/TCP/IP的影响,ARP协议及其通信过程)

    文章目录 1. 以太网(基于碰撞区与碰撞检测的局域网通信标准) 2. 以太网的帧格式(MAC帧) MAC地址,IP地址的区分 MTU MTU对IP协议的影响 MTU对TCP/UDP协议的影响 3.AR ...

  4. linux网络协议栈源码分析 - 邻居子系统邻居状态转移

    1.邻居项状态转移图 邻居项主要的状态转移如下(省略邻居项垃圾回收及转移原因,更权威详细的状态转移图参看<深入理解LINUX网络技术内幕>P648 "图26-13: NUD状态间 ...

  5. 【计算机网络】网络层 : ARP 协议 ( 使用 ARP 协议查找 目的主机 / 路由器 物理地址 )★

    文章目录 一.ARP 协议 二.ARP 协议 使用过程 三.ARP 协议 四种情况 四.ARP 协议规律 五.ARP 协议 计算示例 一.ARP 协议 物理地址需求 : 在 数据链路层 传输数据帧时 ...

  6. 了解TCP协议,IP协议、ICMP协议和ARP协议(TCP报文,TCP的分成管理,TCP与UDP,TCP的三次握手四次挥手原理)

    文章目录 了解TCP/IP协议 TCP报文格式 TCP/IP 的分层管理 TCP与UDP TCP的三次握手与四次挥手 为什么要三次握手? 为什么要四次挥手? IP数据包格式 ICMP协议 ICMP协议 ...

  7. 计算机网络之网络层:4、ARP协议

    网络层:4.ARP协议 ARP协议产生的原因: 同一网络的ARP协议响应过程: 不同网络的ARP协议响应过程: 总结: ARP协议产生的原因: 当网络层交付数据分组给数据链路层时,数据链路层需要对IP ...

  8. 【计算机网络】ARP协议工作原理

    地址解析协议ARP 一 发送数据的过程 在学习ARP协议的工作原理之前,我们需要先知道为什么需要ARP协议,它在数据传输过程中有怎样的作用. 以下是计算机网络中发送数据的一个大致过程. 首先要知道,源 ...

  9. ARP协议详解:了解数据包转发与映射机制背后的原理

    数据来源 一.广播与广播域概述 1.广播与广播域         广播:将广播地址做为目标地址的数据帧         广播域:网络中能接收到同一个广播所有节点的集合(广播域越小越好,收到的垃圾广播越 ...

最新文章

  1. java 拆分类_拆分或不拆分类(用Java)
  2. 【深度学习入门到精通系列】开始恢复更新通知~!
  3. 利用Python编辑一个发送邮件的脚本
  4. 简单易懂棒棒哒的视频传输工具!
  5. Backpropagation 总结
  6. html 编辑xml,编辑XML\HTML时取消浏览“amp”
  7. Oracle 多行合并一行 方法
  8. Docker 实战教程之从入门到提高 (五)
  9. 56、servlet3.0-与SpringMVC整合分析
  10. Modularity QuickStart学习
  11. linux中vim编辑器_为什么Vim爱好者喜欢Herbstluftwm Linux窗口管理器
  12. python中head_Python(Head First)学习笔记:六
  13. string与wstring互转
  14. 5月博客恢复更新的通知
  15. VBXtraLib 1.0 下载
  16. Java学习笔记分享之Dubbo篇
  17. sql server 读写txt文件
  18. Python根据身份证得知性别
  19. JS数据结构中的集合结构详解
  20. 【51单片机】霹雳灯实验代码

热门文章

  1. java swing 多行文本,Java Swing JTextArea
  2. verilog——74HC85四位数值比较器并扩展为16位数值比较器
  3. 标签添加图片Label
  4. 下拉列表(select标签)
  5. 【Socket网络编程进阶与实战】------ Socket网络编程快速入门
  6. 8080端口被占用处理方法
  7. 物联网深度融入生活场景 爆发条件成熟
  8. LruCache源码浅析
  9. linux组态文件,嵌入式Linux组态软件实时数据库的设计
  10. 仿八大行星绕太阳3D旋转效果