千家信息网

LiteOS的SAL及socket编程方法是什么

发表于:2026-11-01 作者:千家信息网编辑
千家信息网最后更新 2026年11月01日,这篇文章主要讲解了"LiteOS的SAL及socket编程方法是什么",文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习"LiteOS的SAL及socket编
千家信息网最后更新 2026年11月01日LiteOS的SAL及socket编程方法是什么

这篇文章主要讲解了"LiteOS的SAL及socket编程方法是什么",文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习"LiteOS的SAL及socket编程方法是什么"吧!

1. SAL套接字抽象层

SAL全称Socket Abstract Layer,即套接字抽象层,主要作用是对上层应用提供一层统一的 socket 编程接口,屏蔽底层网络硬件的差异。

LiteOS的SAL架构如下:

SAL的优势从图中一看即知:

无论底层使用以太网+LwIP协议栈组合,还是使用ESP8266/M26+AT框架组合,经过SAL套接字抽象层之后,对用户提供的接口都是统一的,极大的提高了程序的可移植性。

SAL框架的源码及其实现在SDK中的IoT_LINK_1.0.0\iot_link\network\tcpip目录:

除了sal文件夹之外,其余的文件夹分别对应着不同的sal实现,比如esp8266_socket对应的是基于AT框架和ESP8266的SAL实现。

SAL相关的头文件存放在IoT_LINK_1.0.0\iot_link\inc文件夹中,如图:

  • sal.h:SAL头文件,使用时需包含;

  • sal_imp.h:抽象接口定义头文件;

  • sal_types.h:socket编程中涉及到的类型定义;

  • sal_define.h:socket编程中涉及到的宏定义;

  • link_endian.h:socket编程中的大小端字节序转换函数定义;

2. Socket编程基础

2.1. Socket概述

Socket称为套接字,本质上是一种文件描述符,所以socket通信的过程和操作文件的方法基本类似。

TCP/IP协议族的传输层中,分为有连接的,可靠的TCP传输方式,和无连接的,不可靠的UDP传输方式,所以Socket分为两种:

  • 流式Socket(SOCK_STREAM):提供可靠的、面向连接的通信流,使用TCP协议;

  • 数据报Socket(SOCK_DGRAM):提供一种无连接的服务,使用UDP协议;

2.2. Socket结构体

一个标准的Socket应该包括以下五部分:

  • 协议类型

  • 目的IP

  • 目的端口

  • 源ip

  • 源端口

SAL提供了两种socket的结构体用于存放数据,sockaddr结构体和sockaddr_in结构体,定义均在sal_types.h文件中。

sockaddr结构体的定义如下:

struct sockaddr{    sa_family_t     sa_family;      /* address family, AF_xxx   */    char            sa_data[14];    /* 14 bytes of protocol address */};

参数说明如下:

  • sa_family:地址族,一般为AF_INET,表示IPv4协议;

  • sa_data:包含了源ip、源端口、目的ip、目的端口;

sockaddr_in结构体的定义如下:

struct sockaddr_in{    sa_family_t sin_family;             /* AF_INET */    in_port_t sin_port;                 /* Port number.  */    struct in_addr sin_addr;            /* Internet address.  */    unsigned char sin_zero[8];          /* Pad to size of `struct sockaddr'.  */};

sockaddr结构体将所有的ip和端口信息都放在了sa_data中,不利用编程,而sockaddr_in结构体本质上和sockaddr结构体一样,但是将目的ip和目的端口分离出来,容易编程,所以一般在使用的时候有如下技巧:

使用sockaddr_in结构体赋值,作为参数传递时强制转换为sockaddr类型传递。

2.3. 字节序转换函数

在sockaddr_in结构体中填写sin_port和sin_addr这两个值时,需要注意:

  • in_port_t是uint16_t类型;

  • sin_addr是uint32_t类型;

这样就涉及到了两个转换问题:

  • ip地址的转换

ip地址通常是一个字符串,比如"192.168.1.100",但是此处需要转换为一个uint32_t类型的数据,SAL提供了一个转换函数,在之前提到的link_endian.h文件中,函数如下:


  • 字节序的转换

字节序分为大端存储和小端存储,为了保证统一性,屏蔽硬件差异,需要将ip地址和端口的值转换为网络字节序,SAL提供了本地字节序和网络字节序的互相转换函数,在link_endian.h文件中,其中h表示host主机,n表示network网络字节序:

htonl(unsigned long int hostlong);htons(unisgned short int hostshort);ntohl(unsigned long int netlong);ntohs(unsigned short int netshort);

3. AT框架和SAL配置及开启

本实验中我们使用ESP8266+AT框架+SAL进行实验,所以需要开启使能AT框架和SAL。

3.1. AT框架开启

关于AT框架具体的剖析,可以阅读上一篇教程。

在工程目录下的.sdkconfig中手动配置开启驱动框架(串口使用)和AT框架:

实验中使用的是ESP8266,所以还需要配置路由器的SSID和PASSWD,在SDK目录中的IoT_LINK_1.0.0\iot_link\network\tcpip\esp8266_socket目录下, 打开esp8266_socket_imp.h文件:

在其中设置ESP8266连接的热点名称和密码,这里我的设置如下:

最后,需要修改同文件夹下的esp8266_socket_imp.mk文件,将图中标出的两处TOP_DIR改为SDK_DIR:

3.2. SAL开启

SAL默认是未开启的,需要在工程目录下的.sdkconfig中手动配置开启:

其中CONFIG_TCPIP_ENABLE = y需要自己添加,CONFIG_TCPIP_TYPE宏定义的值目前支持,可以根据自己的需求选择:

  • "lwip_socket"

  • "linux_socket"

  • "macos_socket"

  • "esp8266_socket"

  • "none"

注意:两个宏定义必须同时存在且使能,SAL才会生效。

3.3. SAL自动初始化

使能了SAL之后,系统会自动进行初始化,在SDK目录中的IoT_LINK_1.0.0\iot_link下的link_main.c文件中即可看到:

4. TCP Socket客户端编程实例

4.1. TCP服务端的建立

在本实验中,TCP Server使用网络调试助手模拟,在本机8000端口开启一个TCP服务器,如图:

4.2. SAL提供的Socket客户端编程API

建立socket

API原型如下:

int sal_socket(int domain, int type, int protocol);

参数说明如下:

参数说明常用值
domain协议或地址族AF_INET,表示IPv4
typesocket类型SOCK_STREAM,表示TCP


SOCK_DGRAM,表示UDP
protocol使用的协议号0,表示使用默认协议号
返回值socket描述符int类型值,-1则表示失败

连接服务器socket

API原型如下:

int sal_connect(int sockfd, const struct sockaddr *addr,socklen_t addrlen);

参数说明如下:

参数说明
sockfd创建成功的sockfd描述符
addrsockaddr结构体指针
addrlensockaddr结构体长度

socket发送数据

API原型如下:

int sal_send(int sockfd,const void *buf,size_t len,int flags);

参数说明如下:

参数说明
sockfd创建成功的sockfd描述符
buf发送数据
len发送数据长度
flags发送或接收标记,一般都设为0

socket接收数据(非堵塞)

API原型如下:

int sal_recv(int sockfd,void *buf,size_t len,int flags);

参数说明如下:

参数说明
sockfd创建成功的sockfd描述符
buf接收数据缓冲区
len接收数据缓冲区长度
flags发送或接收标记,一般都设为0

关闭socket

API原型如下:

int sal_closesocket(int sockfd);

参数说明如下:

参数说明
sockfd创建成功的sockfd描述符

4.3. 基于SAL的TCP客户端编程

打开之前创建的HelloWorld工程(如果没有,可以参考第一篇教程新建),创建下面的文件夹sal_test_demo,并在该文件夹中新建一个测试文件sal_tcp_demo.c:

编辑以下内容:

注意,其中的server_ip和server_port应该是服务器的实际情况相对应!

#include #include #define server_port 8000#define server_ip   "192.168.0.101"static int sal_tcp_demo_entry(){    int sockfd;    /* 创建TCP socket */    sockfd = sal_socket(AF_INET, SOCK_STREAM, 0);    if(sockfd < 0)    {        printf("TCP Socket create fail.\r\n");        return -1;    }    else    {        printf("TCP Socket create ok.\r\n");    }    /* 连接服务器 */    struct sockaddr_in server_addr;    server_addr.sin_family = AF_INET;    server_addr.sin_port = htons(server_port);    server_addr.sin_addr.s_addr = inet_addr(server_ip);    while(-1 == sal_connect(sockfd, (struct sockaddr*)&server_addr, sizeof(struct sockaddr)))    {        //连接失败,则1s后自动重连        printf("connect server fail, repeat...\r\n");        osal_task_sleep(1000);    }    printf("connect server ok.\r\n");        int nbytes;    char buf[] = "hello server!";    //发送数据到服务器    nbytes = sal_send(sockfd, buf, sizeof(buf), 0);    if(nbytes < 0)    {        printf("send dat %s fail.\r\n", buf);        return -1;    }    else    {        printf("send [%d] bytes: %s.\r\n", nbytes , buf);    }    //等待接收服务器数据    char recv_buf[50]={0};    while( -1 == (nbytes = sal_recv(sockfd, recv_buf, 50, 0)));    printf("recv [%d] bytes: %s.\r\n", nbytes, recv_buf);    //关闭socket    sal_closesocket(sockfd);    printf("TCP socket closed.\r\n");    return 0;}int standard_app_demo_main(){    osal_task_create("sal_tcp_demo",sal_tcp_demo_entry,NULL,0x800,NULL,12);    return 0;}

然后在user_demo.mk中添加文件路径:

      #example for sal_tcp_demo        ifeq ($(CONFIG_USER_DEMO), "sal_tcp_demo")                  user_demo_src  = ${wildcard $(TOP_DIR)/targets/STM32L431_BearPi/Demos/sal_test_demo/sal_tcp_demo.c}        endif

位置如下:

最后在.sdkconfig中配置选中该demo文件:

然后编译,下载,即可看到串口输出(前提是确保TCP服务器已开启):

在TCP服务端软件也可以看到:

在服务端发送数据,在串口可以看到客户端已接收:

5. UDP Socket客户端编程实例

5.1. UDP服务端的建立

在本实验中,UDP Server使用网络调试助手模拟,在本机8000端口开启一个UDP服务器,如图:

5.2. SAL提供的Socket客户端编程API

连接服务器socket

API原型如下:

int sal_connect(int sockfd, const struct sockaddr *addr,socklen_t addrlen);

参数说明如下:

参数说明
sockfd创建成功的sockfd描述符
addrsockaddr结构体指针
addrlensockaddr结构体长度

发送数据

API原型如下:

int sal_sendto(int sockfd, const void *dataptr, size_t size, int flags,    const struct sockaddr *to, socklen_t tolen);

参数说明如下:

参数说明
sockfd创建成功的sockfd描述符
dataptr待发送的数据指针
size发送包数据大小
flags发送或接收标记,一般都设为0
addrsockaddr结构体指针
addrlensockaddr结构体长度

接收数据

API原型如下:

int sal_recvfrom(int sockfd, void *mem, size_t len, int flags,      struct sockaddr *from, socklen_t *fromlen);

参数说明如下:

参数说明
sockfd创建成功的sockfd描述符
mem接收缓冲区数据指针
size接收数据大小
flags发送或接收标记,一般都设为0
addrsockaddr结构体指针
addrlensockaddr结构体长度

关闭socket

API原型如下:

int sal_closesocket(int sockfd);

参数说明如下:

参数说明
sockfd创建成功的sockfd描述符

5.3. 基于SAL的UDP客户端编程

打开之前创建的HelloWorld工程(如果没有,可以参考第一篇教程新建),创建下面的文件夹sal_test_demo,并在该文件夹中新建一个测试文件sal_udp_demo.c:

编辑以下内容:

注意,其中的server_ip和server_port应该是服务器的实际情况相对应!

#include #include #define server_port 8000#define server_ip   "192.168.0.101"static int sal_udp_demo_entry(){    int sockfd;    /* 创建udp socket */    sockfd = sal_socket(AF_INET, SOCK_DGRAM, 0);    if(sockfd < 0)    {        printf("udp Socket create fail.\r\n");        return -1;    }    else    {        printf("udp Socket create ok.\r\n");    }    /* 服务端信息 */    struct sockaddr_in server_addr;    server_addr.sin_family = AF_INET;    server_addr.sin_port = htons(server_port);    server_addr.sin_addr.s_addr = inet_addr(server_ip);    /* 发送数据到服务器 */    int nbytes;    char buf[] = "hello server!";    nbytes = sal_sendto(sockfd, buf, sizeof(buf), 0, (struct sockaddr*)&server_addr, sizeof(struct sockaddr));    if(nbytes < 0)    {        printf("send dat %s fail.\r\n", buf);        return -1;    }    else    {        printf("send [%d] bytes: %s.\r\n", nbytes , buf);    }    /* 等待接收服务器数据 */    char recv_buf[50]={0};    while( -1 == (nbytes = sal_recvfrom(sockfd, recv_buf, 50, 0,  (struct sockaddr*)&server_addr, sizeof(struct sockaddr))));    printf("recv [%d] bytes: %s.\r\n", nbytes, recv_buf);    /* 关闭socket */    sal_closesocket(sockfd);    printf("udp socket closed.\r\n");    return 0;}int standard_app_demo_main(){    osal_task_create("sal_udp_demo",sal_udp_demo_entry,NULL,0x800,NULL,12);    return 0;}

然后在user_demo.mk中添加文件路径:

      #example for sal_udp_demo        ifeq ($(CONFIG_USER_DEMO), "sal_udp_demo")                  user_demo_src  = ${wildcard $(TOP_DIR)/targets/STM32L431_BearPi/Demos/sal_test_demo/sal_udp_demo.c}        endif

位置如下:

最后在.sdkconfig中配置选中该demo文件:

然后编译,下载,即可看到串口输出(前提是确保UDP服务器已开启):

在UDP服务端软件也可以看到:

在服务端发送数据,在串口可以看到客户端已接收:

感谢各位的阅读,以上就是"LiteOS的SAL及socket编程方法是什么"的内容了,经过本文的学习后,相信大家对LiteOS的SAL及socket编程方法是什么这一问题有了更深刻的体会,具体使用情况还需要大家实践验证。这里是,小编将为大家推送更多相关知识点的文章,欢迎关注!

文件 数据 服务 参数 结构 编程 服务器 框架 原型 端口 成功 字节 客户 客户端 文件夹 类型 指针 目录 目的 网络 数据库的安全要保护哪些东西 数据库安全各自的含义是什么 生产安全数据库录入 数据库的安全性及管理 数据库安全策略包含哪些 海淀数据库安全审计系统 建立农村房屋安全信息数据库 易用的数据库客户端支持安全管理 连接数据库失败ssl安全错误 数据库的锁怎样保障安全 高科技互联网 金融科技还是互联网 互联网科技战争 互联网 科技 凡科互联网科技 融贸通互联网科技 欧洲十大科技互联网公司 语蝶互联网科技有限公司 互联网科技产业特征 燊美集互联网科技有限公司 互联网金融科技名词解释 合肥互联网科技公司公章图片 每天都有哪些好的互联网科技 九思连强互联网电子科技有限公司 中国互联网公司的科技投入 科技和互联网行业的特点 科技互联网公司两年变化 现代科技的标志之一的互联网 恒生科技互联网指数十大成分股 互联网科技快速发展优美句子 互联网科技公司的优势 互联网科技革命 上海喔噻互联网科技 互联网 新科技 安控科技rtu产品工业互联网 阿里巴巴算科技企业还是互联网 健康食品互联网创新科技园项目 新氧科技和美国互联网有关系吗 济南小医互联网科技有限公司招聘 数字互联网科技有限公司
0