例程中有2个port,所以程序中建立了一个主线程,两个子线程。
两个子线程中收到信息之后通过消息队列,发送给主线程进行网络层处理或者转发。
主线程如果需要回复或者转发报文给两个子线程,则向子线程对应的消息队列中发送消息,报文到达子线程中后,取出后发送到相应的端口port上去。
程序流程:
∙ 读取init.cfg配置文件,初始化ROUTER_PORT *head这个port链表
o 读取配置文件中 device_type ,初始化current->type
o 读取配置文件中 device,初始化current->iface,并通过新建socket,并通过ioctl获取设备"eth0"的IP/掩码/硬件址的方式检测是否有效
o 读取配置文件中 port,初始化 current->params.bip_params.port
o 读取配置文件中 network,初始化 current->route_info
o .....
∙ 初始化路由
o 创建主消息队列,初始化每个端口的prot->main_id
o 初始化两个端口的port->func函数指针(router port thread function)
o 初始化port->state = INIT
o 创建两个子线程,此时应该会并发运行两个线程的start_routine函数。
o 设置两个子线程的状态设置为detached,线程运行结束后会自动释放所有资源
∙ 两个子线程的start_routine函数流程dl_ip_thread
o 端口的数据链路层ip(IP_DATA ip_data)初始化,如果失败,设置port->state = INIT_FAILED,返回
1. 初始化ip_data->port
2. 初始化ip_data->local_addr
3. 初始化ip_data->broadcast_addr
4. 建立udp 的Socket,
5. 设置socket属性为:SO_REUSEADDR(设置调用closesocket()后,仍可继续重用该socket。
调用closesocket()一般不会立即关闭socket,而经历TIME_WAIT的过程。),SO_BROADCAST(一般在发送UDP数据报的时候,希望该socket发送的数据具有广播特性)
6. bind the socket to the local port number
7. add bip address to router port structure,
1. memcpy(&port->route_info.mac[0], &ip_data->local_addr.s_addr, 4);
2. memcpy(&port->route_info.mac[4], &port->params.bip_params.port, 2);
3. port->route_info.mac_len = 6;
o 给ip_data分配空间,分配失败,设置port->state = INIT_FAILED,返回
o 为子线程 创建消息队列初始化port->port_id,成功则设置port->state = RUNNING,如果失败,设置port->state=INIT_FAILED,返回
o 进入一个循环,不断从子进程对应的消息队列中取数据
▪ 如果没有取到数据,则从该port的数据链路层取
▪ udp接收数据的时候,采用select,非阻塞的方式来读
▪ 读取到后判断是否是BVLL_TYPE_BACNET_IP类型的BVLL类型的报文,不是的话返回
▪ 然后根据BVLL层的报文类型进入不同的处理:
▪ BVLC_ORIGINAL_UNICAST_NPDU 或者 BVLC_ORIGINAL_BROADCAST_NPDU,判断是否来自自己,如果是自己发送的,丢弃。否则:保存src地址,和ipdata->buff[4]开始的数据到msg_data的src和pdu中。
▪ 收到数据后,初始化msg_data数据的src.len 和 src.adr,初始化msg_storage,将msg_storage发送给主消息队列,主消息队列也会不停的轮询是否有消息到达,有的话会进行处理
▪ 如果取到数据,则说明主进程给该端口的消息队列回复或者转发了消息,需要通过该端口的数据链路层发送出去
▪ 获得目的地址和数据
▪ 数据链路层发送该数据
▪ 根据目的地址(广播或者其他)和数据初始化bip_dest和IP_DATA,组装成BVLL类型的编码
∙ 主进程中不断轮询主消息队列,看是否有子消息队列发来消息
o 如果端口有消息
▪ 获取源消息队列id
▪ 为数据分配空间
▪ 打印收到的数据
▪ 判断是否是网络层数据
▪ 如果是网络层数据
▪ 拷贝消息数据到主线程创建的空间
▪ 解码网络层数据(MSG_DATA * data ->pdu decode dest,BACNET_NPDU_DATA)
▪ 在RouterPort链表中寻源端口srcport
▪ 初始化主进程中维护的msg->data->src = srcport->route_info
▪ 根据不同类型的网络层报文分别进行相应的处理...........比如NETWORK_MESSAGE_WHO_IS_ROUTER_TO_NETWORK等《详细见后面》
▪ 如果不是网络层数据
▪ 拷贝消息数据到主线程创建的空间
▪ 解码网络层数据(MSG_DATA * data ->pdu decode dest,src,BACNET_NPDU_DATA)
▪ 在RouterPort链表中寻源端口srcport
▪ 在路由表中根据目的网络号,寻目的端口
▪ 如果都到相应的源和目的端口则进行下列操作
▪ 初始化主进程中维护的msg->data->src = srcport->route_info
▪ 当报文中有源网络和源地址,且不是广播网络,且该报文中的源网络不是路由器直接相连的网络data->src != addr,则将react面试题中的routerdata->src信息,用最原始的原地址更新(解释:当该路由器收到的报文是由另一个路由器转发的,此时报文中的源地址是最早发送该报文的设备所在网络,而不是该路由器直连端口所对应的网络)
▪ 以上确认好了源地址
▪ 如果目的网络是全局广播网络,或者目的网络和转发端口网络不是同一个网络(还没有到最终的网络),则重新编码npdu(将目的网络和源网络都编码进去)。
▪ 否则目的网络和转发端口网络是同一个网络(已经到达最终的网络),重新编码npdu(只编码源网络,不编码目的网络)
▪ 分配空间给buff,存储新的npdu和原来的apdu数据
▪ 否则需要进行网络搜索(返回后在外层进行搜索发送whois router。。。。)
▪ 释放子线程中分配的msg->data
▪ 如果形成了一个新的npdu数据,则将新组成的数据打印出来,发送给相应的端口
▪ 网络层数据从哪来回哪去
▪ 应用层数据根据目的网络号(非广播)到相应的端口,发送给相应的消息队列(端口)。
▪ 应用层数据是广播的话,则会发送给出源网络之外的所有网络。
▪ 如果没有形成新数据,则说明没有到网络,则需要发送who-is-router-to。。。。。
备注:该router例子没有处理hop count
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论