最近使用 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. Unity3D OpenVR 虚拟现实 保龄球打砖块游戏开发

    据说水哥买了 Valve Index 设备,既然这个设备这么贵,不开发点有(zhi)趣(zhang)游戏就感觉对不起这个设备.本文将来开始着手开发一个可玩性不大,观赏性极强的保龄球打砖块游戏.这仅仅只 ...

  2. C# dotnet 的锁 SemaphoreSlim 和队列

    本文主要是试验在顺序进入等待 SemaphoreSlim 的任务是否会按照顺序经过锁执行 我在一个有趣的WPF程序里面,需要限制任务同时执行的线程数量,不然用户就会说用我的程序会让电脑卡渣.而我的任务 ...

  3. uiautomator2环境搭建+元素定位(安卓)

    一.环境搭建 1.安装uiautomator2 在终端使用pip安装即可 pip install uiautomator2 2.安装adb 可参考:https://www.cnblogs.com/li ...

  4. nim 4. 模块

    看了一下nim的模块系统,真的非常简洁. 1) 一个nim文件就是一个模块 2) 通过import 引入模块,引入的时候不需要带扩展名, 比如有个模块 mod1.nim,  引入的时候: import ...

  5. 仅1.0MB,开源压缩软件7-Zip

    首发微信公众号:SQL数据库运维 原文链接:https://mp.weixin.qq.com/s?__biz=MzI1NTQyNzg3MQ==&mid=2247485212&idx=1 ...

  6. postgresql用sql查询表结构

    查询sql如下: SELECT a.attname AS field, t.typname AS type, CASE WHEN t.typlen = -1 THEN a.atttypmod - 4 ...

  7. cesium教程6-用entity绘制点线面label和billboard广告牌

     注意:billboard显示的图片是立体的,会跟随角度而变化的. pinBuilder.fromText()用于创建自定义地图图钉 完整示例代码: <!DOCTYPE html> < ...

  8. JavaScript字符串对象转JSON格式

    JavaScript字符串对象转JSON格式 原始数据                 {  xAxis: {    type: 'category',    data: ['Mon', 'Tue', ...

  9. Istio(六):Istio弹性(超时&重试)和故障注入

    目录 一.模块概览 二.系统环境 三.弹性(超时&重试) 3.1 弹性 四.故障注入 4.1 故障注入 五.实战:观察错误注入 5.1 在 Grafana.Zipkin 和 Kiali 中观察 ...

  10. .NET快速实现网页数据抓取

    前言 今天我们来讲讲如何使用.NET开源(MIT License)的轻量.灵活.高性能.跨平台的分布式网络爬虫框架DotnetSpider来快速实现网页数据抓取功能. 注意:为了自身安全请在国家法律允 ...