macOS用戶態(tài)進程間的通信實現(xiàn)

macOS內核與用戶態(tài)進程的通信前面已經(jīng)講解過了,用戶態(tài)進程間通信較為簡單,Apple官方推薦使用XPC機制。XPC有底層的C API,也有封裝后的上層的objc和swift API,可以實現(xiàn)像調用自身進程的接口一樣調用對端的暴露接口。這里以swift為例說明XPC的代碼實現(xiàn)。開源項目NuwaStone用戶態(tài)進程間通信均基于XPC實現(xiàn),更多代碼細節(jié)可參考該項目。

對外接口及類定義

使用XPC需新建兩個協(xié)議,作為自身進程和對端進程的通信接口,然后新建一個處理XPC連接請求的類,注意需繼承NSObject,以便實現(xiàn)XPC系統(tǒng)調用所需的代理接口。注意,為了方便XPCConnection類是自身進程與對端進程共用的,但部分方法是不能共用的,詳細見注釋。

// 客戶端進程協(xié)議
@objc protocol ClientXPCProtocol {
}

// 服務端進程協(xié)議
@objc protocol ServerXPCProtocol {
    func connectResponse(_ handler: @escaping (Bool) -> Void)
}

// 連接請求處理類
class XPCConnection: NSObject {
    static let shared = XPCConnection()
    var listener: NSXPCListener?
    // 連接處理對象
    var connection: NSXPCConnection?
    
    private func getMachServiceName(from bundle: Bundle) -> String {
        let clientKeys = bundle.object(forInfoDictionaryKey: ClientName) as? [String: Any]
        let machServiceName = clientKeys?[MachServiceKey] as? String
        return machServiceName ?? ""
    }
    
    // 僅服務端進程調用
    func startListener() {
        let newListener = NSXPCListener(machServiceName: "your service name")
        newListener.delegate = self
        newListener.resume()
        listener = newListener
        Logger(.Info, "Start XPC listener successfully.")
    }
    
    // 僅客戶端進程調用
    func connectToServer(bundle: Bundle, delegate: ClientXPCProtocol, handler: @escaping (Bool) -> Void) {
        guard connection == nil else {
            Logger(.Info, "Client already connected.")
            handler(true)
            return
        }
        guard getMachServiceName(from: bundle) == ClientBundle else {
            handler(false)
            return
        }
        
        let newConnection = NSXPCConnection(machServiceName: DaemonBundle)
        newConnection.exportedObject = delegate
        newConnection.exportedInterface = NSXPCInterface(with: ClientXPCProtocol.self)
        newConnection.remoteObjectInterface = NSXPCInterface(with: DaemonXPCProtocol.self)
        connection = newConnection
        newConnection.resume()
        
        let proxy = newConnection.remoteObjectProxyWithErrorHandler { error in
            Logger(.Error, "Failed to connect with error [\(error)]")
            self.connection?.invalidate()
            self.connection = nil
            handler(false)
        } as? DaemonXPCProtocol
        
        proxy?.connectResponse(handler)
    }
}

XPC的代碼實現(xiàn)是較為簡單的,startListener函數(shù)首先需創(chuàng)建連接所用的Mach端口,網(wǎng)絡資源在Mac內核基本抽象為Mach端口資源,然后設置處理連接請求的代理為自身類,后面需編寫請求處理代碼以校驗連接并建立連接。connectToServer函數(shù)首先判斷是否連接已建立,然后簡單校驗了一下APP包名。創(chuàng)建連接對象時需設置自身暴露接口和遠程調用接口,設置完成后所調用的connectResponse為連接測試函數(shù)。在調用遠程接口前均需獲取remoteObjectProxy遠程對象代理,這里需進行錯誤處理,如果出錯則表示連接斷開。

XPC連接請求系統(tǒng)調用代理接口

startListener函數(shù)中設置處理連接請求的代理為自身類,所以類需要實現(xiàn)代理方法,代理方法的實現(xiàn)很簡單,僅需實現(xiàn)下面一種方法。函數(shù)內的實現(xiàn)看起來與connectToServer差不多,不過多了錯誤處理機制。建立連接前可以進行簽名校驗以增強安全性。這里的invalidationHandler被系統(tǒng)調用時表明連接正常斷開,interruptionHandler被系統(tǒng)調用時表明連接意外斷開,如果有斷開重連要求可以加在這里。

extension XPCConnection: NSXPCListenerDelegate {
    func listener(_ listener: NSXPCListener, shouldAcceptNewConnection newConnection: NSXPCConnection) -> Bool {
        newConnection.exportedObject = self
        newConnection.exportedInterface = NSXPCInterface(with: DaemonXPCProtocol.self)
        newConnection.remoteObjectInterface = NSXPCInterface(with: ClientXPCProtocol.self)
        newConnection.invalidationHandler = {
            self.connection = nil
            Logger(.Info, "Client disconnected.")
        }
        newConnection.interruptionHandler = {
            self.connection = nil
            Logger(.Info, "Client interrupted.")
        }
        
        connection = newConnection
        newConnection.resume()
        return true
    }

服務端對外暴露接口

暴露接口根據(jù)自己的項目需要自行設計實現(xiàn)就好,但要注意這些接口是無返回值的,畢竟是遠程調用,基本也不會需要返回值。這里使用@escaping修飾代碼塊,表示代碼塊的調用可能在函數(shù)返回后。

extension XPCConnection: DaemonXPCProtocol {
    func connectResponse(_ handler: @escaping (Bool) -> Void) {
        Logger(.Info, "Client connected.")
        handler(true)
    }
}
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

友情鏈接更多精彩內容