# FlexibleLink **Repository Path**: Hzhiwei/FlexibleLink ## Basic Information - **Project Name**: FlexibleLink - **Description**: 灵活的轻量化通信协议。 - **Primary Language**: C - **License**: Apache-2.0 - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2020-03-20 - **Last Updated**: 2022-03-02 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README FlexibleLink ## 简介 Flexible Link(FLX)库是个基于比特流的数据传输协议,具有以下特点: * 数据传输形式为点对点,一条完整的传输线包括一个发送器与接收器,接收器与发送器负责数据打包、校验、响应、重传、统计。 * 应用层数据传输是按照帧进行的,一帧数据长度可以不确定,发送端将数据打包后以比特流的形式通过数据链路(串口、TCP等)发送出去,接收器需要从数据链路的流式数据中还原出完整的数据帧。 * 链路有两种数据传输形式:一是纯粹字节流,如串口;二是传输不稳定的数据包,如TCP(不稳定是指发送端发送的一段连续数据在接收端可能被分为多段连续数据,或发送端多段连续数据在接收端可能会被合并为一段连续数据,如TCP的粘包问题)。FLX接收器解析策略有两种:一是接收到一个字节就尝试进行一次解析;二是接收到一段数据一次性进行一次尝试解析。策略特点:策略一二唯一的区别就是策略一重复调用带来的函数调用开销增大,实际解析开销并没有增加;策略的选择与链路数据传输形式有关,建议接收方收到单个字节时使用策略一,收到多个字节时使用策略二。 * 多通道,每个通道完全独立,可以单独设置优先级、可靠性。通道0用于系统数据帧(响应帧),其余可用通道1~255,共255条通道。其中1~255通道用于传输实际应用数据,0通道用于传输FXL自身数据。 * FXL可对传输数据进行统计。 * 通道可单独设定传输优先级。优先级分为5个等级,分别为: ``` FXL_Priority_Idle = 0, FXL_Priority_Low, FXL_Priority_Normal, FXL_Priority_System, FXL_Priority_High, FXL_Priority_Realtime ``` ```FXL_Priority_System```为通道0的系统信息专用优先级。 * 通道可单独设定传输可靠性。可靠性分为3个等级,分别为: ``` FXL_Reliability_NO = 0, //数据帧发送后即不在管理 FXL_Reliability_Response, //数据包发送后接收器需要返回响应 FXL_Reliability_Retry, //数据包发送后如果发送器没有接收到响应则进行重传 ``` 通道0的传输可靠性为```FXL_Reliability_NO```。 * FXL数据传输中如果产生了字节错误,丢失,增加等问题,只会影响发生错误的自己所在的数据帧,不会影响其后的数据。FXL的帧头与帧尾是能够稳定检测到的,因此可以在数据流中确切地分割出单个数据帧。 ``` 发送应用 接收应用 | /|\ \|/ | FXL打包 FXL解包 | /|\ ------------> 传输层 -------------- ``` ## 发送器与接收器 S2FP的发送器与接收器成对使用,发送方必须建立发送器,接收方必须建立接收器,双方发送器与接收器成对使用。 S2FP是单工协议,因此,当A包含发送器,B包含接收的时候,数据帧只能从A发送到B,且不能启用接收响应机制,因为这个机制都要求B向A发送响应数据。 如果启动希望启用接收响应机制,则AB双方都必须建立发送器与接收器,A发送器与B接收器为一对,用于传输A发送至B的数据;B发送器与A接收器为一对,用于传输B发送至A的数据。同时,A的发送器与接收器必须进行链接,B的发送器与接收器也必须进行链接。这样,当其中一方数据通过发送器发送至对方,对方接收器接收到数据后,可通过链接的发送器,将响应数据发送至本方接收器,本方接收器再将响应数据发送给本方链接的发送器,即响应数据回到最初的发送器,发送器判断发送成功。 单工模式: ``` A B 负载数据 发送器 --------------------------------------> 接收器 ``` 双工模式不启用启用接收响应: ``` A B 负载数据 发送器 --------------------------------------> 接收器 负载数据 接收器 <-------------------------------------- 发送器 ``` 单工启用接收响应 ``` A B 负载数据 发送器 --------------------------------------> 接收器 /|\ | | 链接 |链接 | 响应数据 \|/ 接收器 <-------------------------------------- 发送器 ``` 双工启用接收响应 ``` A B A->B负载数据/B->A响应数据 发送器 --------------------------------------> 接收器 /|\ | | 链接 |链接 | B->A负载数据/A->B响应数据 \|/ 接收器 <-------------------------------------- 发送器 ``` 此模式的优点是,发送与接收可以在不同的链路上进行,如:A通过UART发送数据至B,B通过SPI发送数据至A。 接收响应机制可以通知发送方发送状态,统计丢包信息,必要时还能启用超时重传机制保证传输稳定性。 ## 超时重传机制 启用超时重传机制时,必须保证接收响应机制已开启,因为超时重传需要通过接收响应机制来判断发送数据是否超时。 当启用超时重传机制时,必须定义两个参数,一个是最大超时时间,另一个是重传次数。超时时间:发送数据后,超过这个时间未收到数据,认定为超时;最大重传次数:重传超过这个次数后,这帧数据会被彻底丢弃。 发送器每次正常发送数据帧后,会将这一帧数据存进超时重传缓存的一个空位,并记录发送时间,将剩余重传次数设置为最大重传次数。发送器会周期性检查超时重传缓存,如果缓存中存在发送时间与当前时间间隔超过超时时间且剩余重传次数不为零的数据帧记录,则将此数据再次发送出去,同时将这条数据帧记录的剩余重传次数减一,发送时间改为当前时间。当剩余重传次数为0的时候,意味着已经经过足够多次的重传依然失败,此数据被彻底放弃。这个记录的内存即成为了一个空位,后面的数发送后,即可以占用此空位存储。 为防止接收器接收到数据,但返回的响应数据丢失而导致的接收到两次一样的数据,在开启了超时重传机制时,接收器会启用一个接收帧号记录缓存,每接收一个数据,会将数据的帧号与接收时间存储在缓存中。每次接收到数据帧时,会比较数据帧帧号是否包含在缓存中,如果在,则表明前文的情况出现,接收的数据时重复数据,则此数据不会被发送到应用层(但是响应一样会返回给对方)。当接收帧号记录缓存满时,新的帧号会覆盖最早的帧号的位置。但当发送器发送数据过快时,可能会出现缓存迅速填满,很多还在有效期的帧号丢失,导致重复数据出现的风险,因此应该尽可能控制发送频率。 当超时缓存满时,新来的数据会覆盖会剩余重传次数最小的数据,这也表明最早的那个数据(也是重试次数最多的那个数据)被提前丢弃;当多个数据剩余重传次数相同时,会覆盖发送时间距离现在最远的那个数据。 在特殊需求时,也可以一条线路启用超时重传机制,另一条线路不启用。 ## 数据格式 ### 转义字符 链路层的字节流接收时需要区分帧,即需要一个分割字节(BREAKBYTE)用于区分帧的歧视与结束。BREAKBYTE无论取什么值,都可能与实际携带的数据中的字节重复,导致FXL无法明确地进行数据帧分割,因此参考字符串中的```'\'```,引入转义字符。所有需要转义的字符只有两个,一个是转义字符本身,一个是分割符。转义字符的选择应该遵循以下要求:转义字符所表示的字节在实际数据中尽可能少地出现,防止大量实际数据被转义浪费资源。因此转义字符的选择应根据用户的实际情况进行配置。 ### FXL三层模型 应用层:用户应用给出字节序列,具体含义由应用层自行指定。这个字节序列就是要发送的原始数据,此数据会发送至协议层进一步处理。 协议层:协议层数据来源有两种,一是应用层数据,二是系统自身使用的通信数据。应用层调用顶层发送API时,会指出数据发送的通道,FXL会在要发送的数据前增加通道号,系统自动生成的ID号,数据长度信息,在应用层数据后增加校验和信息和分割符,此后数据被发送至链路层。另外,通道0专用于FXL系统自身使用的通信数据,通道1~255用于应用层数据。 链路层数据:链路层数据来自协议层数据,协议层只做一个工作:将协议层数据中的转义字符进行转义。链路层处理完的数据可以直接通过字节流发送接口发送出去。 **协议层数据:** | CH | ID | LEN | PAYLOAD | CRC | BREAK | | :-----------: | :-----: | :---------: | :--------: | :------: | :----: | | 通道号(1-255) | 帧号 | PAYLOAD长度 | 负载数据 | CSRC校验 | 分割符 | | 1 byte | 1 bytes | 1 byte | 0-255 byte | 2 byte | 1 byte | 对于来自应用层的数据,PAYLOAD就是要发送的原始数据,具体格式含义由应用程序自行决定解析。```BREAK```为帧间的分割符,每个帧末以```BREAK```结尾,系统启动后,先发送一个```BREAK```,用于其后第一个数据帧确定帧头。 对于FXL系统数据:PAYLOAD为系统要传输的信息,包括: * 接收响应数据: | PAYLOAD: | RESPONSE-CH | RESPONSE-ID | | :-----------: | :--------------------: | :------------------: | | 负载数据格式: | 要响应的数据帧的通道号 | 要响应的数据帧的帧号 |