【发布时间】:2019-12-10 04:39:17
【问题描述】:
我正在开发一个需要在多个客户端之间进行同步的桌面应用程序。基本上,一群人(比如说 2 到 10 人)都运行同一个应用程序。其中一个托管服务器,其他客户端连接到该服务器。托管服务器的客户端也连接到他自己的服务器。
应用程序应在所有客户端之间保持同步,这意味着所有客户端在应用程序中看到相同的数据。具体来说,我可以用两种不同的形式定义有问题的数据:
- 具有特定值的简单属性(该值必须保持同步)
- 属性列表(列表中的项目及其值必须保持同步)
(1) 的简单示例可能是:客户端当前选择了列表中的哪个项目,以及客户端鼠标指针在应用程序窗口中的当前位置。这些属性不断变化,但这些属性的数量是恒定的并且不会增加(例如在设计时定义)。
(2) 的一个示例可以是聊天消息列表。这些列表会在运行时增长,无法预测会有多少项。
以下是状态、客户端和聊天消息的 C# 示例代码:
public class State
{
// A single value shared between all clients
public int SimpleInteger {get;set;}
// List of connected clients and their individual states
public List<Client> Clients {get;set;}
// List of chat messages
public List<ChatMessage> Messages {get;set;}
}
public class Client
{
public string ClientId {get;set;}
public string Username {get;set;}
public ClientState ClientState {get;set;}
}
public class ClientState
{
public string ClientId {get;set;}
public int SelectedIndex {get;set;}
public int MouseX {get;set;}
public int MouseY {get;set;}
}
public class ChatMessage
{
public string ClientId {get;set;}
public string Message {get;set;}
}
我已经断断续续地研究了很长时间,但是无论我想出什么样的状态同步,它都没有奏效。
当我搜索解决方案时,我只找到游戏的解决方案,但这些解决方案并不是很有帮助,因为我的要求不同:
- 我无法处理“删除更新”,我无法预测(插值或推断)其他客户端正在做什么。每个客户都需要接收每个更新以保持同步。
- 另一方面,我不关心滞后(在合理范围内)。如果我看到其他客户端的更新延迟大约一秒就好了。
- 当新客户端连接(或重新连接)时,必须传输大部分状态(例如:示例 2 中的聊天消息列表)。每个客户都需要了解整个聊天历史记录,因此必须在客户连接时下载。
我目前的解决方案可以总结如下:
- 服务器跟踪状态,例如真相的来源。
- 状态包含需要同步的属性。
- 该状态还包含已连接用户的列表(及其用户名等)。
- 每个客户端还保留一份状态的本地副本,他们可以立即采取行动。例如,它们会不断更新其在本地状态中的鼠标位置。
- 每当客户端更新其本地状态时,此更新都会发送到服务器。
- 这里的潜在例外情况是变化太快的东西,例如鼠标位置,我只会定期发送。
- 服务器还会更新常见的“真实来源”状态。
- 最后,服务器使用新的更新状态更新所有其他客户端。
最后两个步骤是我苦苦挣扎的地方。我可以想到两种同步状态的方法,一种简单但可能效率不高,另一种效率高但容易出错。
- 服务器只是将整个状态发送给所有客户端。
- 服务器一收到来自客户端的更新,就会将更新应用到状态并广播新状态。其他所有客户都会替换他们的本地状态。
- 我觉得这可能会起作用,但是由于“列表”项目(例如聊天消息),状态会迅速增长。在我之前的尝试中,这很快就成为了一个问题,并且将状态发送回变得太慢了。
- 服务器将相同的更新(它收到的)重新发送给所有其他客户端。
- 然后,每个客户端仅将新更新应用到其本地状态以与服务器同步。
- 这可能效率更高,并且仅在客户端连接时才需要发送整个状态。
- 但是,过去我经常遇到客户端不再同步的异步问题。我真的不知道是什么原因造成的,可能是消息之间的冲突(例如服务器告诉客户端更新状态中的值,但客户端只是更新了他的本地值,这有优先权?)。发生这种情况后,一切都完全出错了,因为更新现在被应用于两个不同的状态并产生不同的结果。
我正在寻找有关如何实现此目标的一般概念的指导。我正在使用几个消息传递库来实现客户端和服务器之间的实际通信,我认为这部分不是问题。例如,我可以确保在这些库中接收到每条消息(尽管我不确定订单是否得到保证)。就像我之前说的,延迟不是问题,但我必须保证服务器和所有其他客户端都接收到每个状态更新。
任何帮助都会很棒!谢谢。
【问题讨论】:
标签: server synchronization client desktop-application messaging