本文从 mainIpcServer 相关源码出发了解 VSCode 中的进程间通讯的大致过程
mainIpcServer 的产生
mainIpcServer 在 CodeMain 中的 startup() 中产生:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 class CodeMain { private async startup(args: ParsedArgs): Promise <void > { await instantiationService.invokeFunction(async accessor => { const environmentService = accessor.get(IEnvironmentService); const logService = accessor.get(ILogService); const lifecycleMainService = accessor.get(ILifecycleMainService); const mainIpcServer = await this .doStartup(logService, environmentService, lifecycleMainService, instantiationService, true ); }); } }
这里调用 doStartup 得到 mainIpcServer,它是一个 Server 对象,如果此时没有启动的 Server,该函数会调用 serve 函数启动一个新的 Server,该 Server 监听传入的 mainIPCHandle:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 let server: Server;try { server = await serve(environmentService.mainIPCHandle); once(lifecycleMainService.onWillShutdown)(() => server.dispose()); } catch (error) { } export function serve (hook: any ): Promise <Server > { return new Promise <Server>((c, e ) => { const server = createServer( ); server.on('error', e ); server.listen(hook, ( ) => { server.removeListener('error', e ); c(new Server(server ) ); } ); } );}
产生的 mainIpcServer 作为参数传递给 CodeApplication,并在其 openFirstWindow 函数中注册 Channel:
1 2 3 4 5 6 7 8 instantiationService.createInstance(CodeApplication, mainIpcServer, instanceEnvironment).startup(); const launchMainService = accessor.get(ILaunchMainService);const launchChannel = createChannelReceiver(launchMainService, { disableMarshalling: true });this .mainIpcServer.registerChannel('launch' , launchChannel);
这里首先使用 createChannelReceiver 函数将一个服务包装成一个 Channel,然后调用 mainIpcServer 的 registerChannel 将得到的 Channel 注册到 mainIpcServer。
createChannelReceiver 返回一个 Channel,每个 Channel 都有两个方法,call 和 listen,其功能如下:
call 函数可以调用传入服务包含的所有函数。
listen 可以得到服务的所有事件。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 export function createChannelReceiver (service: unknown, options?: IChannelReceiverOptions ): IServerChannel { const handler = service as { [key: string ]: unknown }; const disableMarshalling = options && options.disableMarshalling; const mapEventNameToEvent = new Map<string , Event<unknown>>(); for (const key in handler) { if (propertyIsEvent(key)) { mapEventNameToEvent.set(key, Event.buffer(handler[key] as Event<unknown>, true )); } } return new class implements IServerChannel { listen<T>(_: unknown, event: string ): Event<T> { const eventImpl = mapEventNameToEvent.get(event); if (eventImpl) { return eventImpl as Event<T>; } throw new Error (`Event not found: ${event} ` ); } call(_: unknown, command: string , args?: any []): Promise <any > { const target = handler[command]; if (typeof target === 'function' ) { if (!disableMarshalling && Array .isArray(args)) { for (let i = 0 ; i < args.length; i++) { args[i] = revive(args[i]); } } return target.apply(handler, args); } throw new Error (`Method not found: ${command} ` ); } }; }
这里暂时不展开 launchMainService 具体做了什么,先分析下 Channel 注册过程。
Channel 的注册
先看一下 mainIpcServer 中的 registerChannel 做了什么:
1 2 3 4 5 6 7 registerChannel(channelName: string , channel: IServerChannel<TContext>): void { this .channels.set(channelName, channel); this ._connections.forEach(connection => { connection.channelServer.registerChannel(channelName, channel); }); }
这里首先把 channel 加入到 channels 列表中,然后对于所有和该服务器相关的连接 ( connection ),也注册该 channel。
connection 在建立新的连接时产生,此时也会将 channels 中的所有 channel 注册到 connection 的 channelServer 中。因此每个 connection 都包含 channels 中的所有 channel。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 class IPCServer { constructor (onDidClientConnect: Event<ClientConnectionEvent> ) { onDidClientConnect(({ protocol, onDidClientDisconnect } ) => { const onFirstMessage = Event.once(protocol.onMessage); onFirstMessage(msg => { const reader = new BufferReader(msg); const ctx = deserialize(reader) as TContext; const channelServer = new ChannelServer(protocol, ctx); const channelClient = new ChannelClient(protocol); this .channels.forEach((channel, name ) => channelServer.registerChannel(name, channel)); const connection: Connection<TContext> = { channelServer, channelClient, ctx }; this ._connections.add(connection); this ._onDidAddConnection.fire(connection); onDidClientDisconnect(() => { channelServer.dispose(); channelClient.dispose(); this ._connections.delete(connection); this ._onDidRemoveConnection.fire(connection); }); }); }); } }
connection 的建立
一个 connection 主要包含三个部分,一个包含一组 channel 的 channelServer 和一个可以向这些 channel 发起请求的 channelClient,以及一个继承自 Client 类的 ctx:
1 2 3 4 5 6 7 8 interface Connection<TContext> extends Client<TContext> { readonly channelServer: ChannelServer<TContext>; readonly channelClient: ChannelClient; } export interface Client<TContext> { readonly ctx: TContext; }
channelServer 和 channelClient 通过一个 protocol 进行通信,protocol 实现了 IMessagePassingProtocol 接口,该接口包含接收消息的事件 onMessage 和发送消息的函数 send:
1 2 3 4 export interface IMessagePassingProtocol { send(buffer: VSBuffer): void ; onMessage: Event<VSBuffer>; }
protocol 提供了 socket 通信能力,可以用于交换如下格式的数据:
1 2 3 4 5 6 7 8 9 10 11 12 13 /-------------------------------|------\ | HEADER | | |-------------------------------| DATA | | TYPE | ID | ACK | DATA_LENGTH | | \-------------------------------|------/ The header is 9 bytes and consists of: - TYPE is 1 byte (ProtocolMessageType) - the message type- ID is 4 bytes (u32be) - the message id (can be 0 to indicate to be ignored)- ACK is 4 bytes (u32be) - the acknowledged message id (can be 0 to indicate to be ignored)- DATA_LENGTH is 4 bytes (u32be) - the length in bytes of DATAOnly Regular messages are counted, other messages are not counted, nor acknowledged.
因此,channelClient 和 channelServer 都通过 onMessage 接收对方传来的消息,使用 send 方法发送消息。
传递的消息
channelClient 传递的消息有四种类型,具体含义如下:
1 2 3 4 5 6 export const enum RequestType { Promise = 100 , PromiseCancel = 101 , EventListen = 102 , EventDispose = 103 }
从 channelClient 可以得到一个 channel,它的 call 和 listen 方法分别用于发起调用方法请求和监听事件请求,相关请求会被包装成消息发送通过 protocol 的 send 方法发出,此时 channelServer 的 onMessage 会处理相关的请求,提取消息中的信息,执行请求的方法和监听相关的事件,完成后还会使用 send 返回相应的响应。
channelServer 会响应五种结果,含义如下:
1 2 3 4 5 6 7 export const enum ResponseType { Initialize = 200 , PromiseSuccess = 201 , PromiseError = 202 , PromiseErrorObj = 203 , EventFire = 204 }
总的来说,可以用下图概括:
1 2 3 4 5 6 7 call / listen call / listen result response < channelClient Message channelServer call / listen do request call / listen
此时对于使用 channelClient 的对象来说可以认为 channelServer 不存在,因为自己调用 channelClient,call 就是执行了 call 的工作,只不过这部分工作是由 channelServer 完成的,自己只是发起请求。
小结
Server 对象维护了一个 Channel 的列表,客户端连接 Server 时可以会创建一个连接 Connection,Connection 中的 channelClient 可以发起请求调用方法或监听事件,请求打包成一条消息发送给 channelServer,channelServer 负责和 Server 的 Channel 交互得到响应的结果,再通过消息将结果返回给 channelClient,至此实现了客户端本地调用服务器上的方法和监听服务器上的事件。