最近使用 STM32 测试了一下 lwip 和 esp8266 的网络连接问题,使用 RTThread 的自动网卡时,发现不能很好的自动切换默认网卡,不能满足需求,所以自己简单的改了一下。

一、准备材料

  • MCU:STM32F103ZT6
  • RTThread:5.0.2
  • 工具:RTThread studio
  • 网络芯片:DM9000(FSMC连接)
  • WIFI:ESP8266(uart连接)

二、测试现象

  • 如果默认网卡断开是,无其他网卡联网时,其他网卡重新联网,不会自动切换默认网卡
  • 初始化时,如果默认网卡为初始成功,即时存在能联网的网卡时,也不会切换默认网卡

三、问题原因

遇到问题的时候,我们首先查看一下官方的说明文档: https://www.rt-thread.org/document/site/#/rt-thread-version/rt-thread-standard/programming-manual/netdev/netdev

找到默认网卡的说明,如下图所示:



从图中不难看出,他只有网卡从 up → down 的时候,才会去切换状态,所以也能解释上面我测试到的现象了

四、解决办法

这里有两个办法可以解决,方式一,直接就该源码;方式二,从新添加相应的处理程序,如下所示:

方式一

程序源码的文件是 rt-thread/components/net/netdev/src/netdev.c,

  1. 先来看一下自动切换网卡的实现方式, 程序如下

    /* Change to the first link_up network interface device automatically */
    static void netdev_auto_change_default(struct netdev *netdev)
    {
    struct netdev *new_netdev = RT_NULL; if (netdev->flags & NETDEV_FLAG_LINK_UP)
    {
    if (!(netdev_default->flags & NETDEV_FLAG_LINK_UP))
    {
    netdev_set_default(netdev);
    }
    return;
    }
    if (rt_memcmp(netdev, netdev_default, sizeof(struct netdev)) == 0)
    {
    new_netdev = netdev_get_first_by_flags(NETDEV_FLAG_LINK_UP);
    if (new_netdev)
    {
    netdev_set_default(new_netdev);
    }
    }
    }

    程序中不难看出切换网卡的思路,确实很厉害,代码也比较简洁,所以切换部分的代码没有问题

  2. 调用的自动切换部分的程序

    /**
    * This function will set network interface device status.
    * @NOTE it can only be called in the network interface device driver.
    *
    * @param netdev the network interface device to change
    * @param is_up the new status
    */
    void netdev_low_level_set_status(struct netdev *netdev, rt_bool_t is_up)
    {
    if (netdev && netdev_is_up(netdev) != is_up)
    {
    if (is_up)
    {
    netdev->flags |= NETDEV_FLAG_UP;
    }
    else
    {
    netdev->flags &= ~NETDEV_FLAG_UP; #ifdef NETDEV_USING_AUTO_DEFAULT
    /* change to the first link_up network interface device automatically */
    netdev_auto_change_default(netdev);
    #endif /* NETDEV_USING_AUTO_DEFAULT */
    } /* execute network interface device status change callback function */
    if (netdev->status_callback)
    {
    netdev->status_callback(netdev, is_up ? NETDEV_CB_STATUS_UP : NETDEV_CB_STATUS_DOWN);
    }
    }
    } /**
    * This function will set network interface device active link status.
    * @NOTE it can only be called in the network interface device driver.
    *
    * @param netdev the network interface device to change
    * @param is_up the new link status
    */
    void netdev_low_level_set_link_status(struct netdev *netdev, rt_bool_t is_up)
    {
    if (netdev && netdev_is_link_up(netdev) != is_up)
    {
    if (is_up)
    {
    netdev->flags |= NETDEV_FLAG_LINK_UP; #ifdef RT_USING_SAL
    /* set network interface device flags to internet up */
    if (netdev_is_up(netdev) && !ip_addr_isany(&(netdev->ip_addr)))
    {
    sal_check_netdev_internet_up(netdev);
    }
    #endif /* RT_USING_SAL */
    }
    else
    {
    netdev->flags &= ~NETDEV_FLAG_LINK_UP; /* set network interface device flags to internet down */
    netdev->flags &= ~NETDEV_FLAG_INTERNET_UP; #ifdef NETDEV_USING_AUTO_DEFAULT
    /* change to the first link_up network interface device automatically */
    netdev_auto_change_default(netdev);
    #endif /* NETDEV_USING_AUTO_DEFAULT */
    } /* execute link status change callback function */
    if (netdev->status_callback)
    {
    netdev->status_callback(netdev, is_up ? NETDEV_CB_STATUS_LINK_UP : NETDEV_CB_STATUS_LINK_DOWN);
    }
    }
    }

    从程序中不难看出,它是只有当网络状态为 down 的时候,才去调用切换网络状态函数

  3. 解决办法

    只需要将 netdev_low_level_set_link_status 函数中的调用位移,移除 down 的判断条件即可解决这个问题。这个问题解决很简单,但是为啥会是这样的设计我还没弄明白,也能是开发人员粗心了(按理说不应该),也有可能是另有深意,但是不满足我得需求,所以还是需要更改一下。这里我没有改源程序,而是从新写了一个函数去实现的,具体流程如下

方式二

通过提供的 API 重写了一下默认网卡切换的方法,程序如下

#include <rtthread.h>
/* 当需要网卡操作是,需要包含这两个头文件 */
#include <arpa/inet.h>
#include <netdev.h> #define DBG_TAG "app_net_monitor"
#include <rtdbg.h> typedef struct net_device_info
{
const char *name; // 设备名称
struct netdev *dev; // 网络设备指针
} net_device_info_t; /* 初始网卡设备,第一个为默认网卡 */
static net_device_info_t net_device[] =
{
{"e0", RT_NULL},
{"esp0", RT_NULL}
}; /* 默认网卡设备 */
struct netdev *network_default = RT_NULL; rt_uint16_t net_dev_number = sizeof(net_device)/sizeof(net_device_info_t); /**
* @brief 自动更改为第一个link_up网络接口设备
*
* @param netdev 网络设备对象
*/
static void network_auto_change_default(struct netdev *netdev)
{
struct netdev *new_netdev = RT_NULL; if (netdev->flags & NETDEV_FLAG_LINK_UP)
{
if (!(network_default->flags & NETDEV_FLAG_LINK_UP))
{
netdev_set_default(netdev);
network_default = netdev;
}
return;
}
if (rt_memcmp(netdev, network_default, sizeof(struct netdev)) == 0)
{
new_netdev = netdev_get_first_by_flags(NETDEV_FLAG_LINK_UP);
if (new_netdev)
{
netdev_set_default(new_netdev);
network_default = new_netdev;
}
}
} /**
* @brief 网络设备状态回调函数
*
* @param netdev 网络设备对象
* @param type 网络状态类型
*/
void network_status_callback(struct netdev *netdev, enum netdev_cb_type type)
{
switch (type)
{
/* 网络断开 */
case NETDEV_CB_STATUS_LINK_DOWN:
network_auto_change_default(netdev);
break; /* 网络连接 */
case NETDEV_CB_STATUS_INTERNET_UP:
network_auto_change_default(netdev);
break; default:
break;
}
} /**
* @brief 返回设备联网状态
*
* @return RT_EOK 表示网络连接
*/
rt_bool_t network_is_up()
{
return netdev_is_link_up(network_default);
} /**
* @brief 初始化自动网卡设置
*
*/
static int app_network_init(void)
{ for (int i = 0; i < net_dev_number; i++)
{
/* 获取网卡设备 */
net_device[i].dev = netdev_get_by_name(net_device[i].name);
if (net_device[i].dev == RT_NULL)
{
LOG_E("not find network interface device name(%s).\n", net_device[i].name);
continue;
} /* 设置网络设备状态变化的回调函数 */
netdev_set_status_callback(net_device[i].dev, network_status_callback); /* 设置一个默认网卡 */
if (network_default == RT_NULL)
{
network_default = net_device[i].dev;
netdev_set_default(network_default);
} } return RT_EOK;
} INIT_APP_EXPORT(app_network_init);

RTThread 自动网卡使用问题的更多相关文章

  1. 从个人的角度谈谈本次GNTC大会的收获

    GNTC资料:from sdnlab 从个人的角度谈谈本次大会的收获 从本次大会的主题演讲来看,目前SDN.NFV的最前沿已经不再像五年前持观望态度以及探讨,各大运营商.各大厂商已经将SDN.NFV具 ...

  2. vmvare入门(1)使用移动,不要使用复制

    1.复制虚拟机会产生新的自动网卡,原来的 System Eth0废了? 2.xftp链接的时候,要选择sftp方式连接,utf8编码.

  3. RT-Thread--内核基础

    内核介绍 内核处于硬件层之上,内核部分包括内核库.实时内核实现. 实时内核的实现包括:对象管理.线程管理及调度器.线程间通信管理.时钟管理及内存管理等等,内核最小的资源占用情况是 3KB ROM,1. ...

  4. Debian、kali类虚拟机网络设置

    Linux发行版分类 先说一下Linux发行版的分类,因为不同的发行版,各自设置的网络的方式就不相同, - 我常用的Linux发行版 -- Debian --- debian --- ubuntu - ...

  5. [dpdk][kernel][driver] 如何让DPDK的UIO开机自动加载到正确的网卡上

    0. 前言 开了虚拟机,开始dpdk之前,我每天都干这几件事: [root@dpdk potatos]# modprobe uio [root@dpdk potatos]# insmod /root/ ...

  6. 解决网卡无法自动获取ip的办法

    解决网卡无法自动获取IP址的方法          为了省钱或者一户多机,很多人都购买宽带路由器共享上网.在架设路由上网的时候,有些“师傅”可能不懂或是偷懒,开启了宽带路由器的DHCP( Dynami ...

  7. Centos 7开启网卡打开DHCP自动获取IP

    在Windows10上安装了CentOS7的Hyper-V虚拟机. 虽然配置了可访问外网的网卡(Win8.1 Hyper-V 共享本机IP上网),但是默认安装的CentOS是没有开启配置网卡信息的,也 ...

  8. CentOS7或CentOS8 开机自动启用网卡的设置方法

    sudo nano /etc/sysconfig/network-scripts/ifcfg-enp0s3(p0s3是网卡,名字不同环境会有差异,输入时可按tab自动补全.) 将最后一行的 ONBOO ...

  9. 【linux】centos6.9设置etc0网卡开机自动获取ip

    在vm新安装的centos系统中,一般选择NAT来设置和主机共享局域网,通过ifconfig etc0 192.168.xx.xx 这种作法机器重启之后就会失效,所以可以使用更改文件的方式完成设置ce ...

  10. 解决eth0网卡无法自动加载的问题

    问题:输入ifup eth0显示无法加载网卡所在的文件 解决办法: vi /etc/rc.d/rc.local 最后一行加入: ifup eth0 然后重启虚拟机即可解决问题. 本文为博主原创文章,未 ...

随机推荐

  1. 001_Cadence软件的安装与介绍

    001_Cadence软件的安装与介绍 软件版本16.6,软件下载:搜索PCB联盟; 安装步骤: 1)   把5个分卷的压缩包解压到同一文件夹; 2)   双击Setup.exe开始安装,先安装Lic ...

  2. 【爬虫实战】用python爬小红书任意话题的笔记,以#杭州亚运会#为例

    目录 一.爬取目标 二.爬虫代码讲解 2.1 分析过程 2.2 爬虫代码 三.演示视频 四.获取完整代码 一.爬取目标 您好!我是@马哥python说,一名10年程序猿. 最近的亚运会大家都看了吗.除 ...

  3. 【图形数据集】CIFAR-10 dataset数据集

    文献引用:https://www.cs.toronto.edu/~kriz/cifar.html The CIFAR-10 dataset The CIFAR-10 dataset consists ...

  4. Go-Zero技能提升:深度探究goctl的妙用,轻松应对微服务开发挑战!(三)

    前言 有位同学在群里说:"Go-Zero官方文档太简洁了,对小白有点不友好.好奇你们是怎么学习的?项目是怎么封装的?有什么提高开发效率的技巧吗?". 来来来,这期内容给你安排上,先 ...

  5. SQL Server实战七:自定义数据类型、标量值、内嵌表值、多语句表值函数的操作

      本文介绍基于Microsoft SQL Server软件,实现数据库用户自定义数据类型的创建.使用与删除,以及标量值.内嵌表值.多语句表值函数等用户定义函数的创建.使用.删除方法. 目录 1 用S ...

  6. IceRPC之传入响应和拦截器

    作者引言 .Net 8.0 下的新RPC 很高兴啊,我们来到了IceRPC之传入响应和拦截器->快乐的RPC, 基础引导,让自已不在迷茫,快乐的畅游世界. 传入响应 Incoming respo ...

  7. 慢查询SQL优化

    记一次慢查询的SQL优化 测试表结构 MariaDB [shoppings]> desc login_userinfo; +------------+-------------+------+- ...

  8. Ceph配置与认证授权

    目录 Ceph配置与认证授权 1. 为什么现在不采用修改配置文件的方式了呢? 2. Ceph元变量 3. 使用命令行修改配置 3.1 全部修改(使用服务名) 3.2 部分修改(修改进程) 3.3 临时 ...

  9. Vue——方法(methods)

    我们用 methods 选项向组件实例添加方法,它应该是一个包含所需方法的对象: <div id="app"></div> <script> c ...

  10. 你不知道的PageContext

    你不知道的PageContext 最近在文艺复兴,学习JSP和Servlet,此文为笔者学习记录. 本文分为以下几个部分: 前言 环境搭建 正文 总结 前言 在我们使用的项目中,存储数据最常用的非 S ...