实时协作:一个支持 RDC 的快速 C++ Windows 库





5.00/5 (16投票s)
轻松创建协作项目
- GitHub: 这是我多线程工具的一部分:https://github.com/WindowsNT/mt
引言
这是一个古老的梦想。我一直想创建这样的应用程序,允许我的所有用户(客户、学生、老师)在同一个文件上工作,并以有效的方式交换实时信息。
也许这也是你的梦想。如果是这样,请继续阅读。
背景
代码是我自己的一些库的集合
- DIFF 库,使用远程差异压缩以实现更有效的更新
- RWMutex 和 tlock,我自己的同步对象,用于提供复杂的锁
- 我的一些小助手,包括
TEVENT
和XSOCKET
,以及 - collab.hpp,这里描述的库
该库采用客户端-服务器模式。服务器监听一个可访问的 TCP 端口,客户端连接到服务器,服务器将所有共享文档保存在本地或内存中。如果客户端更新了文档,服务器会使用更新数据通知所有其他客户端。
客户端身份验证
该库提供了一种抽象的方式来验证服务器或客户端,通过 AUTH
类。这个类的成员函数 Do()
返回一个 HRESULT
。对于 E_PENDING
,该函数将被再次调用。在身份验证成功时,函数必须返回一个 S_
代码,而在失败时,它必须返回一个 E_
代码。
该库提供了一个 ANYAUTH
类,它只返回 S_OK
用于测试目的。您可以传递您自己的身份验证机制。
客户端授权
同样的 AUTH
类的 Do()
函数可以在传递一个文档的 CLSID
时返回 S_FALSE
。在这种情况下,该文件只能以只读方式打开,但客户端对该文件的更新将失败。
该库提供了一个 ANYAUTH
类,它只返回 S_OK
用于测试目的。您可以传递您自己的授权机制。
文档
每个文档由一个 CLSID
唯一标识。每个服务器可以托管无限数量的文档,并且每个客户端可以操作任意数量的文档。
服务器还维护客户端打开的所有文档的当前签名,因此当另一个客户端更新共享文件时,服务器可以知道如何更新客户端。
服务器端
服务器端包含在 SERVER
类中,该类包含所有文档的列表和所有已连接客户端的列表。每个客户端可以打开任意数量的文档。
//
COLLAB::ANYAUTH auth;
COLLAB::SERVER s(&auth, 8765,true); // Port 8765, and true to use filesystem instead of in-memory docs
s.Start(); // Start the server
...
...
...
s.End(); // Ends the server when we want to close it
//
您调用 SERVER::Start()
,如果成功,它将返回 S_OK
。服务器在后台线程中运行。当客户端连接时,会创建一个新线程。客户端向服务器执行以下请求
- 创建或打开文档
- 关闭文档
- 获取文档的当前签名
- 更新文档
当文档被更新时,所有已打开该文档的其他客户端将通过 SERVER::UpdateClientsOfDocument()
方法进行更新。
您也可以调用
void ForceUpdateDocument(GUID c)
以强制将文档更新到所有客户端shared_ptr<DOCUMENT> GetDocument(GUID c)
获取指向文档的指针(如果您想锁定它并在客户端之外编辑它)。
使用 End()
结束服务器。此方法会立即关闭所有客户端。
客户端端
您首先需要一个 ON
结构,它在收到更新时提供通知
class ON
{
public:
virtual void Update(CLSID cid,const char* d,size_t sz) = 0;
};
第一个参数是要更新的文档的 CLSID
。其余的是文档的新数据(一个 DIFF
对象)。
为了重建整个文档,我们可以在多线程 COM 环境中使用此助手
void RecoFromDiff(const char* d, size_t sz, const char* e, size_t sze, vector<char>& o)
{
DIFFLIB::DIFF diff;
DIFFLIB::MemoryRdcFileReader r1(e, sze);
DIFFLIB::MemoryRdcFileReader diffi(d, sz);
DIFFLIB::MemoryDiffWriter dw;
diff.Reconstruct(&r1, &diffi, 0, dw);
o = dw.p();
}
参数 e
和 sze
是我们文档的当前字节数组和大小,而 d
和 sz
是 diff
。该函数在 vector<char>
数组中重建完整的对象。
每个客户端由 COLLAB::CLIENT
类表示,该类包含对所有文档的引用以及 ON
通知类的 vector<>
//
COLLAB::ANYAUTH auth;
COLLAB::CLIENT c1(&auth);
MYON on; // some class that implements Update() of COLLAB::ON
c1.AddOn(&on); // check below for ON class
c1.Connect("localhost",8765);
c1.Open(DOCUMENT_GUID); // If guid does not exist, server creates such a document
//
...
...
...
c1.Close(DOCUMENT_GUID);
...
...
...
c1.RemoveOn(&on);
c1.Disconnect();
如果文档存在,客户端将立即从服务器更新(请注意,如果您有自己的客户端更新版本,则必须在此初始更新之后将其推送到服务器)。
当您想要将更新发送到服务器时,您调用 CLIENT::Put()
HRESULT Put(GUID g, const char* d,size_t sz);
该函数从服务器请求文档签名,然后只将一个 diff
上传到服务器,从而最大限度地减少网络使用。
文件
zip 包含
- 一个完整的协作演示解决方案,包含预构建的可执行文件和源代码
- diff.h 和 collab.hpp 是您需要在自己的项目中包含的全部内容
- 两个预构建的二进制文件,server.exe 和 notepad.exe ,您可以使用它们创建同一文件的多个编辑器
历史
- 2017-6-20:更新了
RWMutex
、tlock
和库 - 2016-12-1:首次发布