实时协作文档系统开发指南

在这个数字化时代,实时协作变得越来越重要。无论是在商业环境中,还是在教育领域,人们都希望能够实时地共享和编辑文档。本文将介绍如何开发一个允许用户实时协作编辑文档的系统。

本文介绍的代码是作者自己编写的一系列库的集合,包括:

  • 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 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 & 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数组中重建完整的对象。

每个客户端由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,最小化网络使用。

文件

压缩包包含:

  • 一个完整的协作演示解决方案,包括预构建的可执行文件和源代码。
  • diff.h和collab.hpp是在自己的项目中需要包含的所有内容。
  • 两个预构建的二进制文件,server.exe和notepad.exe,可以用它们来创建同一个文件的多个编辑器。

2017年6月20日:更新了RWMutex、tlock和库。

沪ICP备2024098111号-1
上海秋旦网络科技中心:上海市奉贤区金大公路8218号1幢 联系电话:17898875485