LiteOS学习笔记-5通信模组之LiteOS的SAL及socket编程
LiteOS学习笔记-5通信模组之LiteOS的SAL及socket编程
- 一、SAL套接字抽象层
- 二、Socket套接字简介
- Socket概述
- Socket结构体
- 字节序转换函数
- ip地址的转换
- 字节序的转换
- 三、编程配置
- 四、编程实例
- SAL提供的Socket客户端编程API
- 建立socket
- 连接服务器
- 发送数据
- 接收数据
- 关闭socket
- 基于SAL的TCP客户端编程
- 基于SAL的UDP客户端编程
- 总结
一、SAL套接字抽象层
SAL全称Socket Abstract Layer,即套接字抽象层,主要作用是对上层应用提供一层统一的 socket 编程接口,屏蔽底层网络硬件的差异。无论底层使用以太网+LwIP协议栈组合,还是使用ESP8266/M26+AT框架组合,经过SAL套接字抽象层之后,对用户提供的接口都是统一的,极大的提高了程序的可移植性。
LiteOS的SAL架构如下:
SAL框架的源码及其实现在SDK中的IoT_LINK\iot_link\network\tcpip
目录:
除了sal文件夹之外,其余的文件夹分别对应着不同的sal实现,比如esp8266_socket对应的是基于AT框架和ESP8266的SAL实现。
SAL相关的头文件存放在IoT_LINK\iot_link\inc文件夹中。
sal.h
:SAL头文件,使用时需包含sal_imp.h
:抽象接口定义头文件sal_types.h
:socket编程中涉及到的类型定义sal_define.h
:socket编程中涉及到的宏定义link_endian.h
:socket编程中的大小端字节序转换函数定义
二、Socket套接字简介
Socket概述
Socket称为套接字,本质上是一种文件描述符,所以socket通信的过程和操作文件的方法基本类似。
TCP/IP协议族的传输层中,分为有连接的,可靠的TCP传输方式,和无连接的,不可靠的UDP传输方式,所以Socket分为两种:
- 流式Socket(SOCK_STREAM):提供可靠的、面向连接的通信流,使用TCP协议
- 数据报Socket(SOCK_DGRAM):提供一种无连接的服务,使用UDP协议
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类型传递。
字节序转换函数
ip地址的转换
ip地址通常是一个字符串,比如"192.168.1.100"
,但是此处需要转换为一个uint32_t类型的数据,SAL提供了一个转换函数,在之前提到的link_endian.h
文件中,函数如下:
//define the normal addres function
#define swaps(value) ((((value)&((uint16_t)0xff00))>>8)|(((value)&((uint16_t)0x00ff))<<8))#define swapl(value) ((((value)&((uint32_t)0xff000000))>>24)|(((value)&((uint32_t)0xff0000))>>8)|\(((value)&((uint32_t)0xff00))<<8)|(((value)&((uint32_t)0xff))<<24))
字节序的转换
字节序分为大端存储和小端存储,为了保证统一性,屏蔽硬件差异,需要将ip地址和端口的值转换为网络字节序,SAL提供了本地字节序和网络字节序的互相转换函数,在link_endian.h
文件中,其中h表示host主机,n表示network网络字节序:
#ifndef htons
#define htons htobes //translate the host endian to network endian (2 Bytes)
#endif#ifndef htonl
#define htonl htobel //translate the host endian to network endian (4 Bytes)
#endif#ifndef ntohs
#define ntohs htobes //translate the network endian to host endian (2 Bytes)
#endif#ifndef ntohl
#define ntohl htobel //translate the network endian to host endian (4 Bytes)
#endif
三、编程配置
使用ESP8266通信模组,需要开启使能AT框架和SAL。
在工程目录下的.config
中手动配置开启驱动框架(串口使用)和AT框架,SAL框架,使能ESP8266,并配置ESP8266参数:
CONFIG_UARTAT_RCVMAX=2048
CONFIG_UARTAT_BAUDRATE=115200
CONFIG_UARTAT_DEVNAME="atdev"CONFIG_AT_ENABLE=y
CONFIG_AT_DEVNAME="atdev"
CONFIG_AT_OOBTABLEN=6
CONFIG_AT_RECVMAXLEN=1024
CONFIG_AT_TASKPRIOR=10CONFIG_DRIVER_ENABLE=y
CONFIG_TCPIP_AL_ENABLE=y
CONFIG_ESP8266_ENABLE=y
CONFIG_ESP8266_SSID="qiushi-dong"
CONFIG_ESP8266_PWD="67996909"
CONFIG_ESP8266_RCVCACHE=1024
CONFIG_ESP8266_CMDTIMEOUT=2000
如下两个宏须同时使能,否则会造成SAL无实现或ESP8266无接口。
CONFIG_TCPIP_AL_ENABLE=y
CONFIG_ESP8266_ENABLE=y
四、编程实例
SAL提供的Socket客户端编程API
建立socket
int sal_socket(int domain, int type, int protocol)
参数 | 说明 | 常用值 |
---|---|---|
domain | 协议或地址族 | AF_INET,表示IPv4 |
type socket | 类型 | SOCK_STREAM,表示TCP |
SOCK_DGRAM,表示UDP | ||
protocol | 使用的协议号 | 0,表示使用默认协议号 |
返回值 | socket描述符 | int类型值,-1则表示失败 |
连接服务器
int sal_connect(int sockfd, const struct sockaddr *addr,socklen_t addrlen)
参数 | 说明 |
---|---|
sockfd | 创建成功的sockfd描述符 |
addr | sockaddr结构体指针 |
addrlen | sockaddr结构体长度 |
发送数据
int sal_send(int sockfd,const void *buf,size_t len,int flags)
参数 | 说明 |
---|---|
sockfd | 创建成功的sockfd描述符 |
buf | 发送数据 |
len | 发送数据长度 |
flags | 发送或接收标记,一般都设为0 |
返回值 | 成功发送数据长度 |
UDP使用
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 |
to | 目标sockaddr结构体指针 |
tolen | 目标sockaddr结构体长度 |
接收数据
非堵塞
int sal_recv(int sockfd,void *buf,size_t len,int flags)
参数 | 说明 |
---|---|
sockfd | 创建成功的sockfd描述符 |
buf | 接收数据缓冲区 |
len | 接收数据缓冲区长度 |
flags | 发送或接收标记,一般都设为0 |
int sal_recvfrom(int sockfd, void *mem, size_t len, int flags,struct sockaddr *from, socklen_t *fromlen)
参数 | 说明 |
---|---|
sockfd | 创建成功的sockfd描述符 |
mem | 接收缓冲区数据指针 |
size | 接收数据大小 |
flags | 发送或接收标记,一般都设为0 |
from | 源sockaddr结构体指针 |
fromlen | 源sockaddr结构体长度 |
关闭socket
int sal_closesocket(int sockfd)
基于SAL的TCP客户端编程
实现函数:
#include <osal.h>
#include <sal.h>#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);//关闭socketsal_closesocket(sockfd);printf("TCP socket closed.\r\n");return 0;
}
基于SAL的UDP客户端编程
实现函数:
#include <osal.h>
#include <sal.h>#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;
}
总结
对比两个客户端实现可知,TCP是可靠的有连接的通信,需要建联后通信,所以创建socket后,需要先connect再进行数据收发;UDP是无连接的,创建socket之后,向指定地址进行收发操作即可。
至此,LiteOS的通信学习完成了,基本流程为通过串口驱动向模组发送AT指令进行操作,将此操作抽象为AT框架,这样,针对不同的指令解析,可以使用统一接口来操作。
针对不同模组进行网络通信时,需要使用套接字编程,liteOS同样提供了SAL抽象了统一接口,不同模组有不同的实现。基于此可以实现运输层的TCP或UDP编程。
LiteOS学习笔记-5通信模组之LiteOS的SAL及socket编程相关推荐
- LiteOS学习笔记-4通信模组之AT框架
LiteOS学习笔记-4通信模组之AT框架 一.AT客户端简介 什么是AT客户端 AT客户端框架作用 二.串口驱动框架实现 2.1串口初始化 2.2读写缓冲区ring_buffer 2.3读写实现与注 ...
- LiteOS通信模组教程05-LiteOS的SAL及socket编程实例
1. SAL套接字抽象层 SAL全称Socket Abstract Layer,即套接字抽象层,主要作用是对上层应用提供一层统一的 socket 编程接口,屏蔽底层网络硬件的差异. LiteOS的SA ...
- LiteOS通信模组教程04-深度剖析LiteOS的AT框架
1. AT客户端框架 在之前的三篇教程中,我们都是直接使用串口助手发送AT指令与模组通信,本篇教程就来探索一下如何使用 MCU 中的串口模组交互. 什么是AT客户端 在使用AT指令的时候,直接发送AT ...
- LiteOS学习笔记-9LiteOS SDK oc流程之MQTT
LiteOS学习笔记-9LiteOS SDK oc流程之MQTT MQTT协议 MQTT协议简介 MQTT消息类型 LiteOS中MQTT实现 LiteOS OC MQTT 抽象组件 概述 配置并连接 ...
- 要闻君说:阿里云联合8家芯片模组商推出“全平台通信模组”;北汽与小桔车服联合“京桔”;IBM要帮助印度公司打造5G战略!...
关注并标星星CSDN云计算 每周三次,打卡即read 更快.更全了解泛云圈精彩news go go go 嗨,大家好!偶是要闻君.盼望着.盼望着,放假的日子又临近了一步,抢先祝福各位看官们新年快乐哈 ...
- 【蓝牙Mesh笔记 ①】ESP32-C3 模组上实现天猫精灵蓝牙 BLE Mesh AliGenie 接入,无需WiFi 连接也可以实现天猫精灵语音控制。
本系列博客学习由非官方人员 半颗心脏 潜心所力所写,仅仅做个人技术交流分享,不做任何商业用途.如有不对之处,请留言,本人及时更改. 系列一:ESP32系列模组基础学习系列笔记 1. 爬坑学习新旅程,虚 ...
- LiteOS学习笔记-1LiteOS内核
LiteOS学习笔记-1LiteOS简介 Lite OS特点 Lite OS内核 LiteOS SDK Huawei IoT Link SDK 1.SDk之OS选择 2.SDk之代码位置 3.SDk之 ...
- 中国移动ML302模组(4G Cat.1 通信模组)TencentOS-tiny AT模组框架适配
关于AT模组框架,之前学习TencentOS-tiny是有写过一篇文章的,链接如下: 还在用传统的方式驱动一个通信模组?不如一起来学习下TOS的AT模组框架吧! 最近查看了TencentOS-tiny ...
- 阿里云联合8家芯片商推“全平台通信模组”,加速物联网生态建设...
因为此次合作,其中部分合作伙伴已经收获了一个月新客户同比线下增长500%的惊人成果. 1月28日晚间消息,阿里云宣布联合业内8家芯片模组商推出"全平台通信模组",帮助用户通过该模组 ...
最新文章
- C/C++、嵌入式秋招之SQL篇
- Linux编写一个C程序HelloWorld
- 周末思考:浅谈如何成为技术一号位?
- js base64编码解码 btoa atob 函数简介
- python读取txt文件-python txt文件的写入和读取
- 自动装配——@Resource(JSR250)和@Inject(JSR330)---[java规范的注解]
- CentOS下创建配置RAID1
- tf.Variable和 tf.get_variable区别(1)
- SQLServer 优化SQL语句 in 和not in的替代方案
- 渗透测试入门8之端口渗透
- JQuery--使用autocomplete控件进行自己主动输入完毕(相当于模糊查询)
- android 崩溃原因,Android SurfaceView常见崩溃的原因以及解决办法
- java中的jackson_Java中的JSON数据绑定框架Jackson使用介绍
- java remote debug parameters
- c#简单注册登录利用缓存存储账号密码_“密码代填”实现单点登录,安全吗?...
- 分屏如何保持运行状态_如何提升心理素质,在比赛中保持更好的状态
- 高通 SD卡驱动代码流程
- 教你用易语言编写一个简单的电脑病毒
- CEEMDAN算法及其应用
- 信号理论: 信号集及其映射
热门文章
- 企业邮箱如何设置邮件模板
- jQ 时间戳的各种转换
- Carrying Conundrum(思维)
- Adapter的作用及用法
- linux 查看声卡设备并测试录音 (ALSA 音频工具)
- 使用python pip安装PyQt5时遇到的不能安装d3dcompiler_47.dll错误
- kestrel web服务器性能对比,Asp.Net Core 3.0 Kestrel服务器下 高性能 WebSocket Server
- Dashgo D1使用手册
- 特斯拉Model3车主称无法开启;传高通开发首款笔记本处理器;Dfinity挑战亚马逊 | 雷锋早报...
- JQuery基础教程:入门