\ n \ r作为停止字节的故障安全性如何?


8

在我的UART通信中,我需要知道所发送消息的起始字节和终止字节。起始字节很容易,但终止字节不是很多。我在消息末尾实现了两个停止字节,即\ n和\ r(十进制的10和13)。UART仅适用于字节0-255的值,那么这有多安全?我可以想象,尽管可能性很小,但当它们不是停止字节时,我的消息可能会依次包含值“ 10和13”。

有没有更好的方法来实现这一目标?


7
要发送任意数据,您必须使用数据包或字节填充。在您的情况下,图案出现在特定位置的概率为1/65536。如果您有足够长的随机数据流,则该值为1。
Oldfart

4
你能提供背景吗?停止位是UART通信的一部分,但是停止字节?这听起来像是一个纯软件问题,并且取决于发送方和接收方已达成的共识。
沃伦·希尔

2
@MariusGulbrandsen如果您的数据确实是任意的并且不是严格的文本(例如ASCII),则null终止将不起作用;您将必须实现一个数据包。
RamblinRose

4
顺便说一句:惯例是将回车符放在换行符: 之前"\x0D\x0A"
阿德里安·麦卡锡

3
@AdrianMcCarthy我认为将其反转的目的是最大程度地降低其成为有效序列的几率。就是说,连续两个Windows行尾将为您提供中间\r\n\r\n\n\r序列...
Mike Caron

Answers:


14

有多种方法可以防止这种情况:

  • 确保您绝不会在常规消息中发送10/13组合(因此仅作为停止字节)。例如发送20 21 22 23 24 25:

20 21 22 23 24 25 10 13

  • 转义符10和13(或带有转义符的所有非ASCII字符,例如。So发送20 21 10 13 25 26发送:(请参阅/信用注释:DanW)

20 21 1b 10 1b 13 25 26

  • 发送消息时定义数据包。例如,如果您要发送消息20 21 22 23 24 25,而不是添加要发送的字节数,则该包为:

<nr_of_data_bytes> <数据>

如果您的消息最大为256字节,请发送:

06 20 21 22 23 24 25

因此,您知道在收到6个数据字节后即结束;您不必再发送10 13。您可以在邮件中发送10 13。如果您的消息可以更长,则可以使用2个字节作为数据大小。

更新1:定义数据包的另一种方法

另一种选择是发送具有特定长度并且可能有很多差异的命令,例如

10 20 30(命令10始终具有2个数据字节)

11 30 40 50(命令11始终具有3个数据字节)

12 06 10 11 12 13 14 15(命令12 + 1字节表示后面的数据字节数)

13 01 02 01 02 03 ...(命令13 + 2个字节(01 02表示256 + 2 = 258个数据字节)

14 80 90 10 13(命令14后跟一个以10 13结尾的ASCII字符串)

更新2:错误的连接/字节丢失

以上所有方法仅在UART线正确发送字节时有效。如果您想使用更可靠的发送方式,也有很多可能性。以下是一些:

  1. 在包内发送校验和(请向Google查询CRC:循环冗余校验)。如果CRC正确,则接收方知道消息已正确发送(概率很高)。
  2. 如果您需要重新发送消息,则需要使用确认(ACK / reply)机制(例如,发送方发送了一些东西,接收方接收到损坏的数据,发送了NACK(未确认),然后发送方可以再次发送。
  3. 超时:如果接收器未及时收到ACK或NACK,则需要重新发送消息。

请注意,上述所有机制可以是您想要的(也可以是简单的)。在重新发送消息的情况下,还需要用于标识消息的机制(例如,将序列号添加到包中)。


1
“确保您不要在常规消息中发送10/13组合(因此仅作为停止字节)。” -你不是说如何发送数据,包括10/13组合-你需要逃避它。因此,“ 20 10 13 23 10 13”可能会以“ 20 1b 10 1b 13 23”的形式发送,并以1b作为转义字符。
Dan W

1
请注意,使用建议的长度字段,当串行链接损坏并丢失单个字节时,您会遇到麻烦。一切都会不同步。
乔纳斯·谢弗

@DanW如果使用前一个或2个字节作为数据字节数,则10或13是这些数据的一部分都没有关系...因此20 10 13 23 10 13可以作为06 20 10 13 23发送10 13其中06是其后的数据字节数。
Michel Keijzers

@MichelKeijzers-是的,但这是您提到的第二个解决方案。您的第一个解决方案缺少对转义序列的说明,以防止停止字节被传输。
Dan W

两种方法都可以使用,并且都可以使用,但是它们有不同的优点和缺点,您可以根据需要添加这些优点和缺点,尽管这超出了OP的要求。
丹W

13

\ n \ r作为停止字节的故障安全性如何?

如果发送,则发送任意数据->可能不够安全。

常见的解决方案是使用转义:

让我们定义字符0x02(STX-帧开始)和0x03(ETX-帧结束)在传输的数据流中必须是唯一的。这样,可以安全地检测到消息的开始和结束。

如果应在消息帧中发送这些字符之一,则将其替换为前缀转义字符(ESC = 0x1b),并在原始字符上添加0x20。

原始字符替换为

0x02 -> 0x1b 0x22  
0x03 -> 0x1b 0x23  
0x1b -> 0x1b 0x3b  

接收者撤消此过程:每当他接收到转义字符时,都会删除该字符,并用0x20减去下一个字符。

这仅增加了一些处理开销,但100%可靠(假设不发生传输错误,您可以/应该通过另外实现校验和机制来进行验证)。


1
好答案。用于ASCII协议的常见转义字符是'\x10'DLE(数据链接转义)。某些Wikipedia页面建议DLE通常以相反的方式使用:说下一个字节是控制字符而不是数据字节。以我的经验,逃脱通常是相反的意思。
阿德里安·麦卡锡

2
这里要注意的一件事是,最坏情况下的缓冲区大小加倍。如果内存确实很紧,那可能不是最佳解决方案。
TechnoSam

1
@Rev为原始字符添加0x20的理由是什么?没有这一点,转义方案也不会奏效吗?
尼克·阿列克谢耶夫

1
@NickAlexeev:如果从流中删除任何其他出现的保留字符,则更容易/更快地标识实际的帧边界。这样,您可以将帧接收和帧解析(包括取消转义)分开。如果您的控制器非常慢,没有FIFO和/或高数据速率,则这可能特别相关。因此,您可以将传入的字节(在STX / ETX之间)在到达时复制到帧缓冲区中,将帧标记为完成并以较低的优先级进行处理。
Rev1.0

@TechnoSam:好点。
Rev1.0

5

您知道,ASCII已经具有用于这些功能的字节。

  • 0x01:标题开始-起始字节
  • 0x02:文本开始-结束标题,开始有效载荷
  • 0x03:文本结尾-结束有效载荷
  • 0x04:传输结束-停止字节
  • 0x17:传输块结束-消息在下一个块中继续

它还在有效载荷内部具有用于各种用途的代码。

  • 0x1b:转义(转义下一个字符-在有效载荷中使用以指示下一个字符不是描述协议中使用的代码的结构之一)
  • 0x1c,0x1d,0x1e,0x1f:分别是文件,组,记录和单位分隔符-用作层次结构数据部分的同时停止和开始字节

您的协议应指定ACK(0x06)和NAK(0x15)的最佳粒度,以便可以重新发送否定的确认数据。降低到这种最好的粒度,明智的做法是在任何(未转义的)开始指示符之后立即添加一个长度字段,并且(如其他答案所述),在任何(未转义的)停止指示符后面加上CRC是明智的。


我将发送任意数据,我想当我不发送ASCII数据时,在我的问题中使用“ \ n \ r”可能会造成混淆。即使我喜欢这个答案,它在通过UART发送ASCII时也非常有用
-CK

@MariusGulbrandsen:只要您的协议确定有效负载在哪里以及每个有效负载部分中必须转义哪些代码,您就可以发送任何内容,而不仅仅是文本数据。
Eric Towers

4

UART就其本质而言并不是故障保护-我们在这里谈论的是1960年代技术。

问题的根源在于UART每10位仅同步一次,从而在这些同步周期之间传递了很多胡言乱语。不同于例如CAN对每个单个位多次采样的CAN。

数据内部发生的任何双位错误都将破坏UART帧并无法通过。开始/停止位中的位错误可能会或可能不会以溢出错误的形式被检测到。

因此,无论使用原始数据还是数据包,EMI引起的位翻转始终有可能导致意外的数据。

存在许多“传统UART quackery”方法可以稍微改善这种情况。您可以添加同步字节,同步位,奇偶校验,双停止位。您可以添加对所有字节的总和进行计数的校验和(然后将其求反,因为为什么不这样),也可以将二进制数作为校验和进行计数。所有这些都被广泛使用,极其不科学并且极有可能丢失错误。但是,这就是人们从1960年代到1990年代所做的事情,并且在今天,诸如此类的生活中发生了许多奇怪的事情。

处理通过UART进行安全传输的最专业方法是在数据包末尾具有16位CRC校验和。其他所有内容都不是很安全,并且很可能会丢失错误。

然后,在硬件级别上,您可以使用差分RS-422 / RS-485来大大提高传输的坚固性。这是长距离安全传输的必要条件。TTL级别UART仅应用于板载通信。RS-232不得用于任何其他目的,而应与旧版本向后兼容。

总体而言,错误检测机制越接近硬件,效果越好。就有效性而言,差分信号最多,其次是检查帧/溢出等错误。CRC16添加了一些,然后“传统UART quackery”添加了一点。


7
此建议是切线的-您尚未真正解决所提出的问题。特别是,您提出的解决方案可能会解决其他问题,但不能解决此页面上问题的基本问题,这是成帧规则和有效负载规则之间的混淆。由于CRC或类似的失败,您的建议最多将拒绝嵌入帧字节的有效数据,而无法进行通信。
克里斯·斯特拉顿

3
实际上,这个答案使情况变得更糟。原来只有数据字节和停止字节。这增加了第三类,CRC字节。如此处所示,这些值可以取任何值,包括{10,13}。
MSalters

1
@MSalters:CRC可以是ASCII编码的十六进制,以防止出现此问题。我在RS485上看到的另一个技巧是在起始/地址字节上设置位7。
晶体管

关于“可以对每个单个位多次采样的CAN”。:位值的实际采样每个位只有一次。您在这里指的是什么?某种错误检查,例如由发送者检查?时钟同步?
Peter Mortensen

进行了校验和的求逆,以便求和整个数据块将得出零,这更容易编码,执行起来也更快。而且,CRC比您在维基百科中查找出来的要好得多。
toolforger '19

0

...我可以想象,尽管可能性很小,但当它们不是停止字节时,我的消息可能会依次包含值“ 10和13”。

在设计串行数据包的格式时,应考虑数据的一部分等于终止序列的情况。要考虑的另一件事是,在传输过程中任何字符都可能损坏或丢失。起始字符,终止字符,数据有效载荷字节,校验和或CRC字节,前向纠错字节不免遭破坏。帧机制必须能够检测数据包何时具有损坏的数据。

有几种方法可以解决所有这些问题。

我做出的工作假设是,数据包仅以串行字节为帧。握手线不用于框架。时间延迟不用于成帧。

发送报文长度

在开头发送数据包的长度,而不是在结尾处发送终止字符(或除此以外)。

优点: 有效载荷以有效的二进制格式发送。

缺点: 在传输开始时需要知道数据包的长度。

转义特殊字符

发送有效载荷数据时转义特殊字符。前面的答案中已经对此进行了解释。

优点: 发送方在传输开始时不需要知道数据包的长度。

缺点: 效率略低,具体取决于需要释放多少有效负载字节。

有效载荷数据经过编码,使其不能包含开始和结束字符

数据包的有效载荷经过编码,因此不能包含开始或结束字符。通常,这是通过发送数字以ASCII或十六进制ASCII表示来完成的。

优点: 普通终端程序易于阅读。无需代码即可处理转义。传输开始时无需知道数据包的长度

缺点: 效率较低。对于有效载荷数据的一个字节,将发送几个字节。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.