串行编程/错误纠正方法
串行编程: 简介和 OSI 网络模型 -- RS-232 布线和连接 -- 典型的 RS232 硬件配置 -- 8250 UART -- DOS -- MAX232 驱动器/接收器系列 -- Windows 中的 TAPI 通信 -- Linux 和 Unix -- Java -- Hayes 兼容调制解调器和 AT 命令 -- 通用串行总线 (USB) -- 形成数据包 -- 错误纠正方法 -- 双向通信 -- 数据包恢复方法 -- 串行数据网络 -- 实际应用开发 -- 串行连接上的 IP
主要有三种处理错误的方式
- 确认或重试 (ACK-NAK)。
- "前向纠错" (FEC)
- 假装从未发生过
每个数据包都会被接收器检查,以确保它是“正常的”。
如果数据包是正常的,接收器(最终)会告诉发送器数据包已成功接收 -- 接收器会确认(ACK)该数据包。
所有版本的 ACK-NAK 绝对需要 双向通信。
发送器会为整个数据包(除了尾部)计算一个校验和或 CRC,然后将其附加到数据包的末尾(在尾部/尾部)。
典型的 CRC 是 32 位,通常是 Fletcher-32 校验和.
旁注:请注意,校验和或 CRC 都是哈希形式,即不可逆地压缩数据。校验和和 CRC 比 MD5 或 SHA 变体等“密码学强”消息认证代码算法更弱。密码学强算法比校验和或 CRC 更善于检测错误,但它们的计算速度更慢。
每当接收器接收到一个数据包时,接收器都会计算完全相同的校验和或 CRC,然后将其与尾部/尾部中的校验和或 CRC 进行比较。如果它们匹配,则整个数据包(几乎可以肯定)是正常的,因此接收器会发送一个 ACK。
如果数据包有任何错误(无论是实际数据中的错误,还是标题中的错误,还是校验和位中的错误 -- 接收器无法识别),接收器会完全丢弃该数据包,并且(在大多数情况下)假装从未看到它。
如果数据包不正常,发送器 会再次发送它。
发送器没有收到 ACK。(所以要么数据包已损坏,要么 ACK 已损坏 -- 发送器无法知道)。
ACK-NAK 最简单的版本是“停止并等待 ARQ”。
发送器发送一个数据包,然后等待一段时间接收 ACK。一旦收到 ACK,它就会立即发送下一个数据包。如果发送器在规定时间内没有收到 ACK,它会从头开始,再次发送同一个数据包,直到收到 ACK。
接收器会等待数据包。如果数据包完美通过所有错误检测测试,接收器会向发送器发送一个 ACK(确认)。
微妙之处:如果接收器完美地接收了数据包,但 ACK 消息延迟太长,则发送器会发送另一个消息副本(“通信回声”。想象一下,数据包包含消息“从弗雷德的账户中扣除 11,000 美元”。当接收器收到这个数据包的第二个副本时,它应该做什么?当然,它应该发送一个 ACK(否则发送器会不断尝试发送这个数据包)。以下两种问题中的一种或两种都可能发生
- 延迟的第一个 ACK 可能会在发送器发送第二个消息副本后到达发送器,因此发送器会发送下一个数据包。然后,第二个 ACK 命中发送器,使发送器误以为“下一个数据包”已成功接收,而实际上并没有。
- 当接收器收到两个相同的消息,都是“从弗雷德的账户中扣除 11,000 美元”时,这是两个合法的独立交易,应该从弗雷德的账户中扣除 22,000 美元吗?或者它仅仅是一次交易,带有一些回声,因此应该只从弗雷德的账户中扣除 11,000 美元?
这两个问题都可以通过添加“序列号”来解决。发送器会跟踪它发送给该接收器的独立数据包的数量,并将该序列号放在每个数据包的标题中。但是,当它重新发送一个数据包时,它会使用相同的序列号重新发送相同的数据包。此外,接收器不会发送一个通用的“ACK”消息,而是通过在其 ACK 消息中添加其序列号来指定它正在响应哪个特定数据包。当存在通信回声时,接收器会看到相同的序列号,因此会 ACK 该序列号(再次),然后丢弃并忽略它已经接收到的数据包的额外冗余副本。当发送器发送一个新的数据包,该数据包恰好包含相同的数据时,接收器会看到一个不同的序列号,因此会 ACK 该新的序列号,并将另外 11,000 美元从弗雷德的账户中扣除。可怜的弗雷德。
对于停止并等待系统,1 位序列号(对于每个新数据包交替 1 - 0 - 1 - 0,以及 ACK1 ACK0 ACK1 ACK0 的响应)就足够了。但是,正如我们将看到的,其他 ARQ 协议需要更大的序列号。
微妙之处:一些早期协议让接收器在接收到不良数据包时向发送器发送一个 NAK(否定确认),发送器会无限期地等待,直到收到 ACK 或 NAK。这是一个坏主意。想象一下,当(a)一些噪声导致数据包损坏,因此接收器将 NAK 发送回发送器,但然后(b)一些噪声导致 NAK 无法识别。或者,想象一个共享介质网络,其中有一个发送器和两个接收器。当一些噪声弄乱了数据包的“目标”字段时会发生什么?
在“停等ARQ”中,发送方和接收方只需要在同一时间将一个数据包保存在内存中。
发送方发送一个数据包,然后发送下一个数据包,再发送下一个数据包,而无需等待。
在发送每个数据包时,它会将该数据包的副本放入一个“窗口”中。
每个数据包都按顺序编号。(序列号必须至少足够大,以唯一标识窗口中的每个数据包)。
... 周转时间 ... 反弹到地球同步卫星 ...
接收方偶尔会发送一个确认消息(“我已经收到所有编号小于等于8980的数据包”, “我已经收到所有编号小于等于8990的数据包”)。
如果接收方正在期待编号为9007的数据包,但它收到了一个 *更早* 的数据包(它已经成功接收过),它会发送(或可能重新发送)一个“我已经收到所有编号小于等于9006的数据包”消息。
当发送方收到“窗口”中任何数据包的确认消息时,它会删除该副本。
当发送方的窗口满了,它会等待一会儿,然后尝试从最旧的数据包开始重新发送窗口中的数据包。
因此,当发送方怀疑某个数据包出现错误时,它会从出错的数据包开始重新发送 *所有* 数据包。这保证了接收方最终会按顺序收到所有数据包。
可选地,如果接收方正在期待编号为9007的数据包,但它收到了编号为9008的数据包,它可能会发送一个针对9007的否定确认 (NAK) 消息,并且忽略任何比它高的数据包编号,直到它收到数据包9007。
当发送方收到“窗口”中任何数据包的NAK消息时,它会从该数据包开始重新传输(并将它保存在“窗口”中)。
在“流式ARQ”中,发送方需要在同一时间将整个“窗口”中的数据包保存在内存中。但接收方仍然只需要一次处理一个数据包,并且按顺序处理它们。
(有些人认为“流式”就像一个使用“停等”协议的、大小等于“窗口”的大数据包,被分成更小的“子数据包”)。
选择性重传ARQ系统是一种流式ARQ。
但它不是接收方一次只处理一个数据包,并丢弃所有比它正在寻找的数据包编号高或低的数据包,而是接收方试图将它接收到的所有数据包的副本保存在它自己的“窗口”中,并与发送方协商,只重新发送 *错误的* 数据包。
如果你只有单向通信,你被迫使用前向纠错,有时称为EDAC(错误检测和纠正)。
你发送数据,然后(而不是CRC)你发送从数据计算出来的“校验位”。
... NASA太空探测器 ... 光盘 ...
最简单的一种是“重复消息”。
如果我发送同一个数据包两次,并且噪声只破坏了其中一个,*并且* 接收方能够识别出哪个被破坏了,那么就没有数据丢失。如果我发送同一个数据包三次,并且噪声破坏了其中任何一个,那么接收方就可以做“最佳3选2”。“校验位”是数据位的两个副本。事实上,噪声可能会破坏 *所有三个* 中的一些位,你仍然可以提取所有数据——将三个数据包并排排列,并对每一位执行“最佳3选2”。只要每个数据包中只有少数位被噪声破坏,并且噪声在每个数据包中的位置不同,就可以恢复所有数据。
... (在这里放图片) ...
有一些非常巧妙的FEC(汉明码、里德-所罗门码),它们可以比“最佳3选2”更好地纠正各种常见的错误,并且只需要与数据位数量相同的“校验位”。
发送方经常实时流式传输音频和视频。
当数据包被破坏时,接收方应该怎么做呢?
如果它向发送方发送一条消息,要求它重新发送该数据包,当回复返回时,可能已经过了几帧视频。使用这些信息已经太迟了。
与其让整部电影暂停,直到请求来回一趟,不如让接收方在默默地接受一些信号降级的同时,将观众的注意力从它身上转移开,这样对观众来说不那么令人反感。接收方会丢弃被破坏的数据包,尽力猜测该数据包中本该有的数据,并继续执行,就好像什么都没发生一样。例如,它可能会用附近像素的颜色填充一个空间。使用这种策略的接收方应该记录它必须填充空白的频率,以便用户可以排查无法持续精确再现连接的问题。
即使他们有双向通信,有时人们也会使用FEC。这样,接收方就可以纠正少量噪声。如果数据包被破坏得太严重,以至于FEC无法修复,协议就会回退到ACK-NAK重传(或“假装从未发生过”)。
一个ACK-NAK协议的详细描述:“XModem / YModem协议参考”,作者Chuck Forsberg,1988年10月14日 https://web.archive.org/web/20070717073025/http://www.commonsoftinc.com:80/Babylon_Cpp/Documentation/Res/KYModemProtocol.htm 从原始版本中存档 http://www.commonsoftinc.com/Babylon_Cpp/Documentation/Res/yModem.htm
一个流式协议的详细描述:“ZMODEM应用程序间文件传输协议”,作者Chuck Forsberg,1988年10月14日 https://web.archive.org/web/20061017125259/http://www.commonsoftinc.com/Babylon_Cpp/Documentation/Res/KZModem.htm 从原始版本中存档 http://www.commonsoftinc.com/Babylon_Cpp/Documentation/Res/zModem.htm
“数据链路错误检测/纠正方法” http://techref.massmind.org/techref/method/errors.htm 简要介绍了几种纠错方法:汉明码、火码、里德-所罗门码、维特比解码等。