# 联 - 网络框架

多数嵌入式操作系统,应具备传输多种网络协议数据的能力,但是网络协议种类繁多,为每种协议都开放单独的接口,势必会增加操作系统使用者的开发负担,亦不利于嵌入式操作系统的普及与推广。

XiUOS网络框架采用以网络适配器为中心的抽象方式,发送与接收数据时,只需要像Linux socket网络通信一样,仅关注发送与接收的数据,而不用把重点放在所使用的具体协议上。这种抽象方式有效屏蔽了种类繁多的网络协议细节,对外提供统一的网络接口,简化了基于嵌入式操作系统的网络开发。网络框架对各类协议的网络设备进行了抽象:

  • 一个网络协议设备,被抽象为一个Adapter

Adapter可以被设计为采用类似面向对象的方法,针对不同的网络协议,扩展其数据成员与接口,从而为种类繁多的网络协议实现统一的管理架构。在网络应用开发的过程中,只需要使用对应网络协议的Adapter实例,而无需关心网络协议的硬件细节,从而实现了网络数据收发功能与底层硬件的解耦。

LIAN IMAGE

# 1. XiUOS网络框架关键数据结构定义和解析

  • struct Adapter结构
struct Adapter
{
    char name[NAME_NUM_MAX];
    int fd;

    int product_info_flag;
    struct AdapterProductInfo *info;
    ATAgentType agent;

    struct Socket socket;

    int net_role_id;
    enum NetProtocolType net_protocol;
    enum NetRoleType net_role;
    enum AdapterStatus adapter_status;

    char buffer[ADAPTER_BUFFSIZE];
    
    void *done;
    void *adapter_param;

    struct DoublelistNode link;
};

name成员是一个可读的名字,用于唯一标识一个Adapter结构。fd成员是一个标识符编号,用于唯一标识此网络设备所用串口。product_info_flag用于根据型号给设备进行分类。AdapterProductInfo结构体包含该网络设备的一些信息,如厂家名vendor与型号product_model。

struct AdapterProductInfo
{
    uint32_t functions;
    char vendor_name[NAME_NUM_MAX];
    char model_name[NAME_NUM_MAX];
    uint32_t work_mode;

    void *model_done;
};

其中model_done成员包含统一的API,用于对特定网络适配器进行实际的数据读写。model_done成员有IpProtocolDone/PrivProtocolDone两套接口,根据网络适配器种类而定,两套接口都具有的基础API如下。在使用一个网络适配器前后需要打开(open)/关闭(close)该网络适配器,打开适配器后需要配置(ioctl)其属性,setup和setdown用于使能/关闭网络传输功能,setaddr/setdns/setdhcp/ping/netstat为一些基础打网络调试接口,send/recv分别用于从网络适配器接收数据与向网络适配器发送数据,connect/disconnect用于建立/断开一个网络连接。

struct IpProtocolDone/PrivProtocolDone
{
    int (*open)(struct Adapter *adapter);
    int (*close)(struct Adapter *adapter);
    int (*ioctl)(struct Adapter *adapter, int cmd, void *args);
    int (*setup)(struct Adapter *adapter);
    int (*setdown)(struct Adapter *adapter);
    int (*setaddr)(struct Adapter *adapter, const char *ip, const char *gateway, const char *netmask);
    int (*setdns)(struct Adapter *adapter, const char *dns_addr, uint8 dns_count);
    int (*setdhcp)(struct Adapter *adapter, int enable);
    int (*ping)(struct Adapter *adapter, const char *destination);
    int (*netstat)(struct Adapter *adapter);
    int (*connect)(struct Adapter *adapter, enum NetRoleType net_role, const char *ip, const char *port, enum IpType ip_type);
    int (*send)(struct Adapter *adapter, const void *buf, size_t len);
    int (*recv)(struct Adapter *adapter, void *buf, size_t len);
    int (*disconnect)(struct Adapter *adapter);
};

agent结构体是串口收发数据结构,用于辅助串口通信。网络设备通过agent通信,具有两个缓冲区分别用于向CPU接收和发送信息。

struct ATAgent
{
    char agent_name[64];
    int fd;

    char *maintain_buffer;
    uint32 maintain_len;
    uint32 maintain_max;
    
    int lock;

    ATReplyType reply;
    char reply_lr_end;
    char reply_end_last_char;
    char reply_end_char;
    uint32 reply_char_num;
    int rsp_sem;

    pthread_t at_handler;

    #define ENTM_RECV_MAX 256
    char entm_recv_buf[ENTM_RECV_MAX];
    uint32 entm_recv_len;
    enum ReceiveMode receive_mode;
    int entm_rx_notice;
};
typedef struct ATAgent *ATAgentType;

socket成员用于实现上层通信协议。建立多个安全稳定的tcp/udp套接字通信。

struct Socket
{
    uint8_t type;                /* socket type:DGRAM->UDP,STREAM->TCP */
    uint8_t protocal;            /* udp or tcp */
    unsigned short listen_port;  /* 0-65535 */
    uint8_t socket_id;           
    uint8_t recv_control;       
    uint8_t af_type;             /* IPv4 or IPv6 */
    char *src_ip_addr;           /* source ip address */  
    char *dst_ip_addr;           /* destination IP address */
};

net_protocol成员用于指示此网络设备支持的协议,IP_PROTOCOL类型表示支持IP层相关协议,PRIVATE_PROTOCOL表示此网络设备支持额外的协议。net_role_id用于辅助net_protocol,用于标识区分该协议下支持的多个设备。

enum NetProtocolType
{
    PRIVATE_PROTOCOL = 1,
    IP_PROTOCOL,
    PROTOCOL_NONE,
};

net_role成员用于设置此网络设备通信过程中的模式和角色,包括主、从、路由、终端等。

enum NetRoleType
{
    CLIENT = 1, 
    SERVER,
    MASTER,
    SLAVE,
    COORDINATOR,
    ROUTER,
    END_DEVICE,
    ROLE_NONE,
};

adapter_status成员用来指示网络设备注册状态。buffer/done/adapter_param成员均为预留给adapter使用的数据缓冲区和用户数据接口。

enum AdapterStatus
{
    REGISTERED = 1,
    UNREGISTERED,
    INSTALL,
    UNINSTALL,
};

最后,在系统中每种网络协议的Adapter被分别组织成不同双链表,如Lora的Adapter链表、4G的Adapter链表等,使用的链表节点即为link成员。

# 2. XiUOS网络框架驱动开发

以WiFi网络适配器为例。网络框架针对每个具体的网络适配器填充Adapter的数据成员项,如协议类型、设备名称、厂商信息等。数据成员填充完后,将其注册。

int AdapterWifiRegister(struct Adapter *adapter);

#define ADAPTER_WIFI_NAME "wifi"

static int AdapterWifiRegister(struct Adapter *adapter)
{
    int ret = 0;

    strncpy(adapter->name, ADAPTER_WIFI_NAME, 32);

    adapter->net_protocol = IP_PROTOCOL;
    adapter->adapter_status = UNREGISTERED;

    ret = AdapterDeviceRegister(adapter);
    if (ret < 0) {
        printf("AdapterWifi register error\n");
        return -1;
    }

    return ret;
}

WiFi设备协议类型为IP_PROTOCOL,故使用IpProtocolDone接口注册到model_done。实现model_done中的数据通信API,具体实现细节取决于所使用网络协议,无法实现的API可以置为NULL:

static const struct IpProtocolDone hfa21_done =
{
    .open = Hfa21Open,
    .close =  Hfa21Close,
    .ioctl = Hfa21Ioctl,
    .setup = Hfa21SetUp,
    .setdown = Hfa21SetDown,
    .setaddr = Hfa21SetAddr,
    .setdns = NULL,
    .setdhcp = NULL,
    .ping = Hfa21Ping,
    .netstat = Hfa21Netstat,
    .connect = Hfa21Connect,
    .send = Hfa21Send,
    .recv = Hfa21Receive,
    .disconnect = NULL,
};

将hfa21_done接口注册到model_done:

AdapterProductInfoType Hfa21WifiAttach(struct Adapter *adapter)
{
    struct AdapterProductInfo *product_info = PrivMalloc(sizeof(struct AdapterProductInfo));
    if (!product_info) {
        printf("Hfa21WifiAttach Attach malloc product_info error\n");
        PrivFree(product_info);
        return NULL;
    }

    strcpy(product_info->model_name, ADAPTER_WIFI_HFA21);

    product_info->model_done = (void *)&hfa21_wifi_done;

    return product_info;
}

# 3. XiUOS网络框架的使用实例

网络应用开发者根据自己的需求,将网络框架提供的API加上一些逻辑,封装为一套应用层API,使用该套API操作网络适配器,应用层API可以分为通用API与协议特有API。通用API用于网络适配器的打开、关闭、参数配置以及网络测试功能,协议特有API用于特定协议的配置以及数据收发。以Wifi网络适配器为例:

/* generic API: open/close a adapter instance */
int AdapterDeviceOpen(struct Adapter *adapter);
int AdapterDeviceClose(struct Adapter *adapter);

/* generic API: configure a adapter instance */
int AdapterDeviceControl(struct Adapter *adapter, int cmd, void *args);

/* generic API: network interface */
int AdapterDeviceSetAddr(struct Adapter *adapter, const char *ip, const char *gateway, const char *netmask);
int AdapterDeviceSetDns(struct Adapter *adapter, const char *dns_addr, uint8 dns_count);
int AdapterDeviceSetDhcp(struct Adapter *adapter, int enable);
int AdapterDevicePing(struct Adapter *adapter, const char *destination);
int AdapterDeviceNetstat(struct Adapter *adapter);

/* Wifi API: get current Wifi adapter reading and receiving */
ssize_t AdapterDeviceSend(struct Adapter *adapter, const void *src, size_t len);
ssize_t AdapterDeviceRecv(struct Adapter *adapter, void *dst, size_t len);

在获取数据前,需要先打开要使用的网络适配器;网络适配器打开后对其进行相关参数如波特率等的配置,接着配置相关网络参数后,对网络适配器数据可以随时进行读取;使用完毕后,须关闭网络适配器。完整的使用过程示例如下:

int AdapterWifiTest(void)
{
    char cmd[64];
    int baud_rate = BAUD_RATE_57600;

    struct Adapter* adapter =  AdapterDeviceFindByName(ADAPTER_WIFI_NAME);

    AdapterDeviceOpen(adapter);
    AdapterDeviceControl(adapter, OPE_INT, &baud_rate);

    AdapterDeviceSetUp(adapter);
    AdapterDeviceSetAddr(adapter, "192.168.66.253", "255.255.255.0", "192.168.66.1");
    AdapterDevicePing(adapter, "36.152.44.95");
    AdapterDeviceNetstat(adapter);

    const char *ip = "192.168.66.211";
    const char *port = "12345";
    enum NetRoleType net_role = CLIENT;
    enum IpType ip_type = IPV4;
    AdapterDeviceConnect(adapter, net_role, ip, port, ip_type);

    const char *wifi_msg = "LiuKai Test";
    int len = strlen(wifi_msg);
    for(int i = 0;i < 10; ++i) {
        AdapterDeviceSend(adapter, wifi_msg, len);
        PrivTaskDelay(4000);
    }

    char wifi_recv_msg[128];
    while (1) {
        AdapterDeviceRecv(adapter, wifi_recv_msg, 128);
    }
    
}

# 4. XiUOS自组网功能简介

传统的数据收集与上报系统架构,一般采用星型拓扑的组织形式,以网关为中心,将数据收集设备(传感器)连接组织起来,传感器之间一般没有大规模的数据流动。而这种中心化的网络架构,有其自身的缺点,如中心节点的数据压力经常逼近极限,成为整个系统的性能瓶颈,或者一旦程序崩溃,即造成其承载的所有传感器发生掉线事故。 所以我们在提供中心化网络服务的同时,也提供了去中心化的自组网络服务,自组网结构一般具有以下优点:

1、动态接入
传统的中心节点型网络,需要网关手动配置想要连接的传感器,然后对区域进行轮询扫描,传感器则在被轮询到时,被动地上报自己的数据。而自组网新节点需接入时,可实现主动接入,无需对网关进行手动配置,节省运维成本。
2、去中心化
自组网内无中心节点,节点之间可进行自主路由协商,以此来实现跳转通信,相比传统中心化网络,有效分摊网络流量,进而分散节点运算压力。自组网具有良好的健壮性,任何一个节点发生崩溃,其所承载的传感器会自动寻找其他上传节点,不会造成大面积的掉线事故。
3、不依赖现有网络
节点之间通信,不依赖现有的网络基础设施,自组网内部实现网络自治,以此应对通信地点和时间的不确定性。

Last Updated: 11/25/2021, 4:00:10 PM