iOS處理序間通訊之CFMessagePort

來源:互聯網
上載者:User

標籤:越獄開發   通過   系統   returns   類型   htm   gid   ret   encoding   

iOS處理序間通訊之CFMessagePort 

iOS系統是出了名的封閉,每個應用的活動範圍被嚴格地限制在各自的沙箱中。儘管如此,iOS還是提供了若干處理序間通訊機制,CFMessagePort就是其中之一。

從類名可以看出,CFMessagePort屬於Core Foundation層的東西,其實現部分是開源的,代碼在可以在蘋果的開原始碼庫中找到。

使用方式

1、訊息接收者

CFMessagePort連接埠訊息的接收者需要實現以下功能:

1.1 註冊監聽

訊息接收者需要通過以下方式註冊訊息監聽:

-(void)startListenning{  if (0 != mMsgPortListenner && CFMessagePortIsValid(mMsgPortListenner))  {           CFMessagePortInvalidate(mMsgPortListenner);        }      mMsgPortListenner = CFMessagePortCreateLocal(kCFAllocatorDefault,CFSTR(LOCAL_MACH_PORT_NAME),onRecvMessageCallBack, NULL, NULL);        CFRunLoopSourceRef source = CFMessagePortCreateRunLoopSource(kCFAllocatorDefault, mMsgPortListenner, 0);        CFRunLoopAddSource(CFRunLoopGetCurrent(), source, kCFRunLoopCommonModes);        NSLog(@"start listenning");}

其中LOCAL_MACH_PORT_NAME的定義為:

#define LOCAL_MACH_PORT_NAME    "com.wangzz.demo"

經過查看源碼發現,CFMessagePort實際上是通過mach port實現的。Mach port是iOS系統提供的基於連接埠的輸入源,可用於線程或進程間通訊。而Runloop支援的輸入源類型中就包括基於連接埠的輸入源,因此可以使用Runloop做為CFMessagePort連接埠源事件的監聽者。

上述代碼有幾點需要說明:

  • 通過CFMessagePortCreateLocal可以建立一個本地CFMessagePortRef對象

  • CFMessagePort對象是靠一個字串來唯一標識的,這一點非常重要,在這裡字串是由宏LOCAL_MACH_PORT_NAME定義的;

  • 建立CFMessagePort對象的同時設定了連接埠源事件的回呼函數onRecvMessageCallBack,用於處理連接埠源事件;

  • 將建立的對象作為輸入源添加到Runloop中,從而實現對連接埠源事件的監聽,當Runloop收到對應的連接埠源事件時,會調用上一步中指定的回調方法;

1.2 實現回調方法

回呼函數為CFMessagePortCallBack類型,其定義部分為:

typedef CFDataRef (*CFMessagePortCallBack) (   CFMessagePortRef local,      SInt32 msgid,      CFDataRef data,      void *info);

各個參數的含義為:

  • CFMessagePortRef local

當前接收訊息的CFMessagePortRef對象。

  • SInt32 msgid

這個欄位非常有用,用於標識訊息。如果通訊雙方進程約定號每個msgid對應的資料結構,即可實現較為複雜的通訊。

  • CFDataRef data

通訊的真正資料部分。

  • void *info

為使用CFMessagePortCreateLocal方法建立port連接埠時指定的CFMessagePortContext對象的info欄位,通常為空白。

該回調方法可以返回一個CFDataRef類型的資料給port訊息的寄件者,以實現有效雙方通訊,這一點也非常重要。

我的回呼函數onRecvMessageCallBack的實現:

CFDataRef onRecvMessageCallBack(CFMessagePortRef local,SInt32 msgid,CFDataRef cfData, void*info){    NSLog(@"onRecvMessageCallBack is called");        NSString *strData = nil;        if (cfData)    {          const UInt8  * recvedMsg = CFDataGetBytePtr(cfData);                strData = [NSString stringWithCString:(char *)recvedMsg encoding:NSUTF8StringEncoding];              /**                  實現資料解析操作                  **/                 NSLog(@"receive message:%@",strData);    }    //為了測試,產生返回資料    NSString *returnString = [NSString stringWithFormat:@"i have receive:%@",strData];        const char* cStr = [returnString UTF8String];        NSUInteger ulen = [returnString lengthOfBytesUsingEncoding:NSUTF8StringEncoding];      CFDataRef sgReturn = CFDataCreate(NULL, (UInt8 *)cStr, ulen);        return sgReturn;}

該方法實現的較為簡單,解析約定的資料(測試代碼中約定傳送的是string),為了測試,同時產生一個CFDataRef資料返回給port訊息的寄件者。

1.3 取消連接埠監聽

可以通過如下方式取消對port連接埠的監聽:

- (void)endLisenning{    CFMessagePortInvalidate(mMsgPortListenner);        CFRelease(mMsgPortListenner);}

CFMessagePortInvalidate會停止port訊息的發送和接收操作,而只有調用了CFRelease,CFMessagePortRef對象才真正的被釋放掉。

2、訊息寄件者

發送部分代碼如下:

-(NSString *)sendMessageToDameonWith:(id)msgInfo msgID:(NSInteger)msgid{    // 產生Remote port    CFMessagePortRef bRemote = CFMessagePortCreateRemote(kCFAllocatorDefault, CFSTR(MACH_PORT_REMOTE));        if (nil == bRemote) {            NSLog(@"bRemote create failed");                return nil;    }        // 構建發送資料(string)    NSString    *msg = [NSString stringWithFormat:@"%@",msgInfo];        NSLog(@"send msg is :%@",msg);        const char *message = [msg UTF8String];        CFDataRef data,recvData = nil;        data = CFDataCreate(NULL, (UInt8 *)message, strlen(message));        // 執行發送操作    CFMessagePortSendRequest(bRemote, msgid, data, 0, 100 , kCFRunLoopDefaultMode, &recvData);        if (nil == recvData) {            NSLog(@"recvData date is nil.");                CFRelease(data);                CFMessagePortInvalidate(bRemote);                CFRelease(bRemote);                return nil;    }    // 解析返回資料    const UInt8  * recvedMsg = CFDataGetBytePtr(recvData);        if (nil == recvedMsg) {            NSLog(@"receive date err.");                CFRelease(data);                CFMessagePortInvalidate(bRemote);                CFRelease(bRemote);                return nil;    }    NSString    *strMsg = [NSString stringWithCString:(char *)recvedMsg encoding:NSUTF8StringEncoding];        NSLog(@"%@",strMsg);        CFRelease(data);        CFMessagePortInvalidate(bRemote);        CFRelease(bRemote);        CFRelease(recvData);        return strMsg;}

其中MACH_PORT_REMOTE的定義為:

#define MACH_PORT_REMOTE    "com.wangzz.demo"

發送訊息時要相對簡單,首先通過CFMessagePortCreateRemote產生一個Remote的CFMessagePortRef,這裡需要注意的是CFMessagePortCreateRemote時傳入的字串唯一標識MACH_PORT_REMOTE必須和訊息接收者建立local的CFMessagePortRef時使用的字串唯一標識是同一個!

通過查看源碼發現,CFMessagePortCreateRemote會根據MACH_PORT_REMOTE定義的字串為唯一標識擷取訊息接收者通過CFMessagePortCreateLocal使用相同字串建立的底層mach port連接埠,從而實現向訊息接收者發送資訊。

如果訊息接收者還沒有建立或者通過CFMessagePortCreateLocal建立local連接埠失敗時,想要通過CFMessagePortCreateRemote去建立remote連接埠肯定是失敗的。

說明

  • 很遺憾的是,在iOS7及以後系統中,CFMessagePort的通訊機制不再可用。

在使用CFMessagePortCreateLocal/CFMessagePortCreateRemote建立CFMessagePortRef對象時會失敗,官方文檔中是這麼說的:

This method is not available on iOS 7 and later—it will return NULL and log a sandbox violation in syslog. See Concurrency Programming Guide for possible replacement technologies.
  • CFMessagePort只能用於本地進程通訊。

  • CFMessagePort是基於mach port連接埠的通訊方式,不但可以用於進程通訊,也可以用於線程間通訊,只是線程間通訊有了GCD和Cocoa提供的原生方法,已經能很方便的實現了,沒必要再使用CFMessagePort。

  • 進程通訊使用情境

iOS系統多任務機制,使得處理序間通訊基本都只能用于越獄開發。常用的情境是前端有一個UI程式用於介面展示,後端有一個daemo精靈程式用於任務處理。

demo工程

特地做了了個demo工程,以便更好地示範CFMessagePort的使用,可以到CSDN下載。

為了類比處理序間通訊情境,我將訊息接收進程CFMessagePortReceive做成了能夠後台播放音樂的程式,以便其切到後台後能繼續存活。

由於CFMessagePort不再支援iOS7及以後系統,本demo實在iOS6系統上測試的。

demo使用方式:

  • CFMessagePortReceive啟動後,點擊Start Listenning建立CFMessagePort介面並開始監聽port訊息,然後將CFMessagePortReceive切到後台;

  • 啟動CFMessagePortSend程式,在輸入框中寫入內容,點擊發送按鈕即可和CFMessagePortReceive通訊。

  • MessagePort通訊過程中會有日誌輸出,可以使用以下方式查看日誌:

1.真機

選擇:Xcode->Window->Organizer->Devices,然後選中視窗左側當前裝置的Console視窗查看。

2.模擬器

選擇:模擬器->調試->開啟系統日誌,或者直接使用快速鍵?/直接開啟系統控制台查看日誌。

參考文檔

  • CF-855.14

  • Threading Programming Guide

  • CFMessagePort Reference

iOS處理序間通訊之CFMessagePort

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.