This commit is contained in:
呈祥
2026-01-06 19:49:07 +08:00
parent 210d948353
commit 68ad8b3726
6 changed files with 233 additions and 571 deletions
+27 -7
View File
@@ -6,6 +6,7 @@ import { getDemoServerInstance } from "./server";
const server = getDemoServerInstance();
export class Client {
public name: string = "";
// 当前文档
public document: string = "";
// 当前服务端文档状态
@@ -16,12 +17,15 @@ export class Client {
public outstanding: TextOperation | null = null;
// 本地缓存的待发送操作队列(包含outstanding
public buffer: TextOperation[] = [];
// 是否启用同步到服务端
public enableSync: boolean = true;
constructor() {
// 模拟连接服务器,获取初始文档状态和修订版本号
const { document, revision } = server.connect(this);
this.revision = revision;
this.document = document;
this.name = `client-${Math.random().toString(36).substring(2, 15)}`;
}
// 当用户更改文档时调用此方法
@@ -33,18 +37,22 @@ export class Client {
// 接收到来自服务器的新操作,可能是另一个客户端的操作,也可能是服务端确认本地操作的响应
applyServer(revision: number, operation: TextOperation): void {
if (revision < 0 || this.revision < revision) {
if (revision < 0 || this.revision > revision) {
throw new Error("operation revision not in history");
}
const isSelfOp = operation.id === this.outstanding?.id;
for (let i = 0; i < this.buffer.length; i++) {
// TODO: check
// 将buffer中的操作转换为基于服务端文档状态的操作
this.buffer[i] = TextOperation.transform(this.buffer[i], this.buffer[i - 1] || operation)[0];
const newOp = TextOperation.transform(this.buffer[i], this.buffer[i - 1] || operation);
newOp.id = this.buffer[i].id;
this.buffer[i] = newOp;
}
// 本地重新应用buffer中的操作,合并其它客户端的操作到本地
let newDoc = this.syncedDoc;
newDoc = operation.apply(newDoc);
for (let i = 0; i < this.buffer.length; i++) {
newDoc = this.buffer[i].apply(newDoc);
}
@@ -55,29 +63,41 @@ export class Client {
this.revision = revision;
// 操作来自服务端确认本地操作的响应
if (operation.id === this.outstanding?.id) {
this.outstanding = null;
if (isSelfOp) {
this.serverAck();
}
console.log('mattuy', 'client', this.name, this)
}
// 服务端已确认收到发送的操作,从buffer中移除该操作,尝试发送下一个
serverAck(): void {
this.outstanding = null;
this.buffer.shift();
this.sendOperationIfNeed();
}
// 逐个向服务端发送本地待提交的操作。发送后等待服务端确认,触发serverAck方法后继续发送下一个操作
async sendOperationIfNeed() {
// 如果禁用了同步,则不向服务端发送操作
if (!this.enableSync) {
return;
}
if (this.outstanding || this.buffer.length === 0) {
// 有正在发送的操作,或者没有待提交的操作,no-op
return;
}
// 向服务端发送本地待提交的操作,这里没有实现操作压缩,我们一个一个发送
this.outstanding = this.buffer[0];
this.outstanding = this.buffer.shift()!;
// 模拟向服务端发送操作,实际实现应该通过网络
server.receiveOperation(this.revision, this.outstanding);
// 应该有超时重发机制,这里没有实现
}
// 设置是否启用同步到服务端
setEnableSync(enabled: boolean): void {
this.enableSync = enabled;
// 如果重新启用同步,尝试发送待发送的操作
if (enabled) {
this.sendOperationIfNeed();
}
}
}