工作原理
SAL 组件主要实现的两个功能:支持多个协议栈接入和统一抽象接口函数。对于不同的协议栈或网络功能实现,网络接口的名称可能各不相同,以 connect 连接函数为例,lwIP 协议栈中接口名称为 lwip_connect ,而 AT Socket 网络实现中接口名称为 at_connect。SAL 组件提供对不同协议栈或网络实现接口的抽象和统一,组件在 socket 创建时通过判断传入的协议簇(domain)类型来判断使用的协议栈或网络功能,完成 RT-Thread 系统中多协议的接入与使用。
- int socket(int domain, int type, int protocol);
上述为标准 BSD Socket API 中 socket 创建函数的定义,其中 domain
表示协议域又称为协议簇(family),用于判断使用哪种协议栈或网络实现,AT Socket 网络实现的协议簇类型为 AF_AT
,lwIP 协议栈使用协议簇类型有 AF_INET
、AF_INET6
等。开启 lwIP 协议栈支持后,使用 AF_INET 创建网络套接字,则此套接字底层使用 lwIP 协议栈函数实现。AT Socket 是 RT-Thread 自主研发的基于 AT 组件的网络功能实现,其设备的连接和数据的通讯都是通过 AT 命令完成,支持标准 BSD Socket API 。
目前 RT-Thread 系统中网络软件包或网络功能的 socket 创建函数中协议簇类型固定,若要支持不同的协议栈或网络实现需要修改传入的协议簇类型。为了适配不同协议栈或网络实现,SAL 组件中对于每个协议栈或者网络实现提供两种协议簇类型匹配方式:主协议簇类型和次协议簇类型。socket 创建时先判断传入协议簇类型是否存在已经支持的主协议类型,如果是则使用对应协议栈或网络实现,如果不是判断次协议簇类型是否支持。目前系统支持协议簇类型如下:
- lwIP 协议栈: family = AF_INET、sec_family = AF_INET
- AT Socket 网络实现: family = AF_AT、sec_family = AF_INET
对于 SAL 组件中协议簇的支持,组件中每个协议簇类型存在一个协议簇结构体,由协议簇结构体列表统一管理,协议簇结构体中定义了该协议栈的执行函数,如 lwIP 协议栈中的 lwip_socket()、lwip_connect() 等。 SAL 组件中创建的每个 socket 也存在结构体定义,由 socket 结构体列表统一管理,socket 结构体中存放当前 socket 的执行函数和基本信息,在 socket 创建时通过判断传入协议簇类型,注册对应协议簇执行函数到 socket 结构体执行函数中。之后,使用该 socket 进行函数操作时,主要是获取 socket 结构体中的协议簇执行函数,完成函数操作。如下为 SAL 组件中 connect 函数抽象实现示例:
- /* SAL 组件为应用层提供的标准 BSD Socket API */
- int connect(int s, const struct sockaddr _name, socklen_t namelen){
- /* 获取 SAL 套接字描述符 */
- int socket = dfs_net_getsocket(s);
- /* 通过 SAL 套接字描述符执行 sal_connect 函数 */
- returnsal_connect(socket,name,namelen);
- }
- /* SAL 组件抽象函数接口实现 */
- int sal_connect(int socket, const struct sockaddr _name, socklen_t namelen){
- struct socket _sock;
- /* 通过 SAL 套接字描述符获取 socket 结构体 */
- sock=sal_get_socket(socket);
- if(!sock)
- {
- return-1;
- }
- if(sock->ops->connect==RT_NULL)
- {
- return-RT_ENOSYS;
- }
- /* 调用获取结构体中对应的协议簇执行函数 */
- returnsock->ops->connect((int)sock->user_data,name,namelen);
- }
- /* 协议簇执行函数调用底层 lwIP 协议栈函数实现 */
- int lwip_connect(int socket, const struct sockaddr *name, socklen_t namelen){ ...}