|
发表于 2018-6-6 01:32:52
|
显示全部楼层
本帖最后由 dukelec 于 2018-6-6 01:57 编辑
楼主这种主机不确定的情况下的地址分配可以参照 IPv6 的地址自动分配方案,我之前有精简出一个可以用于 RS485 的协议,相对于被动地址自动分配的方案还是复杂很多,所以我现在不怎么用(现在用的比较多的是楼上楼上的被动地址自动分配的方案,不敢贴楼号,怕被和谐)。
协议大概是这么定义的:
- /*
- * Neighbor Discovery for IP version 6 (IPv6)
- *
- * Refer to:
- * tools.ietf.org/html/rfc4861
- *
- * Only support NS and NA Message, and compressed in 6LoCD format
- *
- * We don't use solicited-node multicast address,
- * it's useless for CDBUS, so we use link-local address instead.
- *
- * The source and dest address for NA message are inverted,
- * because we don't want interrupt all nodes on the same net.
- */
- /*
- * NS type:
- * MAC (IP): 255 (::) -> [XX] (fe80::[XX])
- * pkt_type = LO_NH_ICMP;
- * icmp_type = ND_TYPE_NS;
- * 6 bytes identify
- *
- * NA type: (send to self MAC, prevent filtering)
- * MAC (IP): 255 (::) -> [XX] (fe80::[XX])
- * pkt_type = LO_NH_ICMP;
- * icmp_type = ND_TYPE_NA;
- * 6 bytes identify
- * byte0: RSO = 'bx00
- *
- *
- * P.S.:
- * [XX] is current MAC or target MAC, and it's not equal to 255
- *
- * 6 bytes identify: (determine whether the packet is sent by us)
- * only use first 4 bytes currently
- */
- /*
- * Avoid send collision by lower layer (always enable if sender id = 255):
- * wait for bus idle
- * wait a random time, if bus busy once, go back to: "wait for bus idle"
- * send packet
- *
- * Address negotiation work flow (periodic execution):
- *
- * BEGIN:
- * target_addr = self_mac == 255 ? gen_random_addr() : self_mac
- * identify = random uint32 number
- * set_mac_filter(target_addr)
- * next: SEND_NS
- *
- * SEND_NS:
- * send NS packet
- * next: WAIT_RX
- *
- * WAIT_RX:
- * if self_mac == 255:
- * if receive NS or NA from others:
- * next: BEGIN
- * else if self NS timeout: (bus data collision)
- * next: SEND_NS
- * else if wait NA timeout:
- * update self_mac with target_addr
- * next: IDLE
- *
- * else self_mac != 255:
- * while receive NS from others:
- * send NA back
- * if receive NA from others:
- * set self_mac = 255
- * next: BEGIN
- * else if self NS timeout:
- * next: SEND_NS
- * else if wait NA from others timeout:
- * next: IDLE (keep current address)
- *
- * IDLE:
- * while receive NS from other node:
- * send NA back
- * // avoid received NA
- */
复制代码
每个设备上电的时候都可以没有地址(本机地址为 255 代表没有分配到地址,因为正常使用的时候 255 是广播地址);
没有地址的设备上电为自己自动随机分配一个 0~254 之间的地址,但不可以直接使用,要先确保地址没有其它人选用;
譬如某个节点随机选择的 id 为 44, 那么就发送一个 255 -> 44 的 NS 数据包(IPv6 协议中 NS 是询问,NA 是应答),并包含一个随机生成的 6 字节的 identify 号码,
只有自己先收到含同样 identify 号码的数据包(表示发送的时候大概没有冲突),然后也没有收到同样地址是 255 -> 44 的 NA 包,也没有收到其它人节点发送的同样地址的 NS 包,那么才可以采用此地址。
如果收到相同地址别人的 NA 回复或 NS 请求,那么则放弃重新选择 ID.
然而如果已经分配到 ID, 那么在收到别人的 NS 请求时,将回复相同 identify 号的 NA 告诉对方有人占用此地址。如果检测到的是别人的 NA, 那么就要放弃已经获得的地址从头来过。
已经分配到 ID 的节点,每隔一段时间也要发个 NS 包检查一下,防止偶发因素没有检测到冲突。
然后对于楼主的要求,一个对等的无 ID 冲突的网络,选出一个主机就很容易了,只需要一个节点(谁速度快是谁)发一个广播包告知天下自己是王,如果已经有王了,那就被夺权,或是比地址大小决定谁胜出,胜出者重新发包告知天下。这种方式,一般上电之后等待一段时间,确保大家 ID 都分好了再进行这个步骤,譬如规定大家分到 ID 后都等 10 秒钟。收到其它人宣告后自动取消自己的宣告打算。
顺便说一下,上面之所以是 255 -> 44 而不是 44 -> 255 是因为 CDBUS 可以使用硬件控制器增强,硬件会过滤掉不是发送给自己的数据包,因此为减少对其它不相干节点的打扰,所以才不使用 44 -> 255 的广播包。
顺便再说下,CDBUS 的硬件增强可以自动避免冲突,跟 CAN 的原理一样,所以可以支持多主对等通讯,而且速率至少可以达到 10Mbps, 数据包也没有 CAN 的 8 个字节限制,方便很多。 |
|