本文从 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
,至此实现了客户端本地调用服务器上的方法和监听服务器上的事件。