【发布时间】:2016-07-14 17:55:37
【问题描述】:
我正在寻找可能的技术来优雅地处理网络协议设计中的竞争条件。我发现在某些情况下,要同步两个节点以进入特定的协议状态是特别困难的。这是一个存在此类问题的示例协议。
假设 A 和 B 处于 ESTABLISHED 状态并交换数据。 A 或 B 发送的所有消息都使用单调递增的序列号,这样 A 可以知道 B 发送消息的顺序,A 可以知道 B 发送消息的顺序。或者 B 可以向另一个发送 ACTION_1 消息,以进入需要严格按顺序交换消息的不同状态:
发送 ACTION_1 接收 ACTION_2 发送 ACTION_3
但是,A 和 B 可能同时发送 ACTION_1 消息,导致他们都收到 ACTION_1 消息,而他们希望收到 ACTION_2 消息作为发送 ACTION_1 的结果。
这里有几种可能的处理方式:
1) 将 ACTION_1 发送到 ACTION_1_SENT 后更改状态。如果我们在这种状态下收到 ACTION_1,我们会检测到竞态条件,并继续仲裁谁可以开始序列。但是,我不知道如何公平地仲裁这一点。由于两端很可能会同时检测到竞态条件,因此随后的任何操作都容易出现其他类似的竞态条件,例如再次发送 ACTION_1。
2) 复制整个消息序列。如果我们在 ACTION_1_SENT 状态下收到 ACTION_1,我们会将其他 ACTION_1 消息的数据包含在 ACTION_2 消息中,等等。这只有在不需要决定谁是动作的“所有者”时才有效,因为两端都会最终对彼此执行相同的操作。
3) 使用绝对时间戳,但是准确的时间同步根本不是一件容易的事。
4)使用灯时钟,但据我了解,这些仅对因果相关的事件有用。由于在这种情况下 ACTION_1 消息没有因果关系,我看不出它如何帮助解决找出哪个最先发生以丢弃第二个的问题。
5) 使用某种预定义的方式来丢弃两端收到的两条消息之一。但是,我找不到没有缺陷的方法来做到这一点。一个天真的想法是在两边都包含一个随机数,并选择具有最高编号的消息作为“获胜者”,丢弃具有最低编号的消息。但是,如果两个数字相等,我们就会打成平手,然后我们需要另一种方法来从中恢复。一个可能的改进是在连接时处理一次仲裁并重复类似的顺序,直到两个“获胜”之一,将其标记为最喜欢的。每次出现平局时,最爱的一方获胜。
有人对如何处理这个问题有进一步的想法吗?
编辑:
这是我想出的当前解决方案。由于我找不到 100% 安全的方法来防止连接,我决定让我的协议在连接序列期间选择一个“最喜欢的”。选择这个最爱需要打破可能的联系,但在这种情况下,协议将允许多次尝试选择最爱,直到达成共识。 After the favorite is elected, all further ties are resolved by favoring the elected favorite.这将可能与协议的单个部分联系起来的问题隔离开来。
关于选举过程中的公平性,我根据每个客户端/服务器数据包中发送的两个值编写了一些相当简单的东西。在这种情况下,这个数字是一个以随机值开头的序列号,但它们可以是任何数字,只要这些数字是相当随机的。
当客户端和服务器必须解决冲突时,它们都使用 send(它们的值)和 recv(另一个值)值调用此函数。收藏夹调用此函数,并将收藏夹参数设置为 TRUE。这个函数保证在两端给出相反的结果,这样就可以在不重新发送新消息的情况下打破平局。
BOOL ResolveConflict(BOOL favorite, UINT32 sendVal, UINT32 recvVal)
{
BOOL winner;
int sendDiff;
int recvDiff;
UINT32 xorVal;
xorVal = sendVal ^ recvVal;
sendDiff = (xorVal < sendVal) ? sendVal - xorVal : xorVal - sendVal;
recvDiff = (xorVal < recvVal) ? recvVal - xorVal : xorVal - recvVal;
if (sendDiff != recvDiff)
winner = (sendDiff < recvDiff) ? TRUE : FALSE; /* closest value to xorVal wins */
else
winner = favorite; /* break tie, make favorite win */
return winner;
}
假设两端发送ACTION_1消息后进入ACTION_1_SENT状态。两者都会收到 ACTION_1_SENT 状态的 ACTION_1 消息,但只有一个会获胜。失败者接受 ACTION_1 消息并进入 ACTION_1_RCVD 状态,而获胜者丢弃传入的 ACTION_1 消息。序列的其余部分继续进行,就好像失败者从未在与获胜者的竞争条件下发送 ACTION_1。
让我知道您的想法,以及如何进一步改进。
【问题讨论】:
-
ACTION_1 和 ACTION_2 实际上是干什么用的?我知道你想让这个问题变得通用,但它可以澄清一些事情,以了解它们在你的系统中的实际用途。
-
在这种情况下,它是用于同步剪贴板的协议。每次应用程序在剪贴板上粘贴内容时,它都会成为剪贴板的所有者并通知另一端。但是,一次只能有一个剪贴板所有者,并且两端可能同时尝试成为所有者。在这种情况下,只有一个人应该获胜以避免系统中的不一致。
标签: networking concurrency protocols race-condition