開始使用ZMQ

安裝大坑跳過之後,接下來的是另外一個大坑,先說ZMQ的所有請求都是阻塞式的,因此使用你必須開一個線程來管理,再者ZMQ沒有Success跟Fail的Call back,完全靠伺服器回給你的參數自己判斷是成功還是失敗,所以理所當然的也不會有HTTP Status Code,第三就是ZMQ沒有連線逾時的判斷,他的重連機制會一直連一直連,直到連上為止,已Socket來說這是一個非常方便的功能,但是正如前言所說,連登入都要使用ZMQ了,如果一直重連的話,不就卡死了嗎?所以針對ZMQ流程我自己想出了以下規劃,希望有更好方法的網友也能提出來大家一起學習研究:

既然沒有Success跟Fail的Call Back,那我們只好自己動手做:

先舉例有一個測試的API叫做SeverTime,我們首先在Model的.h檔加入:

沒錯~非常眼熟,跟AFNetworking一模一樣。

再來.m檔一樣加入:

到這邊我們前置作業就算完成了

接著是關於Req/Rep模式,相當於一般的網路請求,送出Req,拿到Rep:

- (void)severTime {

    ZMQContext *ctx = [[ZMQContext alloc] initWithIOThreads:1]; //似乎可以指定線程,但我沒試過
    NSString *endpoint = 這裡是伺服器IP位置;
    ZMQSocket *requester = [ctx socketWithType:ZMQ_REQ]; //Socket模式為Req

    BOOL didConnect = [requester connectToEndpoint:endpoint];
    if (!didConnect) {
      NSLog(@"*** Failed to connect to endpoint [%@].", endpoint);
    }

    int kMaxRequest = 1;

    NSString *severTimeRequest = [NSString stringWithFormat:@"{\"__api\":\"ServerTime\"}"]; //請求的參數
    NSData *request = [severTimeRequest dataUsingEncoding:NSUTF8StringEncoding];

    for (int request_nbr = 0; request_nbr < kMaxRequest; ++request_nbr) {

        [requester sendData:request withFlags:0];
        NSLog(@"Sending request %d.", request_nbr);

        NSData *reply = [requester receiveDataWithFlags:0];
        NSMutableDictionary *jsondata = [NSJSONSerialization JSONObjectWithData:reply options:0 error:nil]; //拿到reply就把他轉成NSMutableDictionary來使用

        /*
          這邊成功會回傳
          {
              success = 1;
              severTime = XXXXXXXXX;
          }
          失敗則是
          {
              success = 0;
              errMsg = "";
          }
        */

        NSNumber *success = jsondata[@"success"]; //所以我們可以透過success來判斷

        switch (success.integerValue) {
            case 0:
            {
                self.failBlock(jsondata); //0就塞進失敗的Block
            }
                break;

            case 1:
            {
                self.finishBlock(jsondata); //1就塞進成功的Block
            }
                break;

            default:
                break;
        }
    }
    [ctx closeSockets]; 使用完後就關閉ZMQ Socket
}

一般使用也是跟AFNetworking一樣:

[[SeverTime requestWithFinishBlock:^(id object) {

    NSLog(@"successPbject : %@",object);

}failBlock:^(id failObject) {

    NSLog(@"failObject : %@",failObject);

}] severTime];

但正如我之前所說的,他是阻塞式的請求,而且沒有Timeout,因此我們的流程要變成這樣:

先寫一個Timeout的方法,裡面時間到就把執行序關掉,因為必須做到手動控制,所以請使用NSThread。

登入的Button按下後會有一個透明淺灰的BlockView把整個畫面擋住不讓使用者做任何動作,接著開啟一個延時執行的Timer,時間到就把它關掉,然後開啟執行序,執行上面包裝好的請求方法,不管成功或是失敗,都要先把請求的執行序關掉,然後回到主線程:

 [self performSelectorOnMainThread:@selector(dismissBlockView) withObject:nil waitUntilDone:YES];

關掉延時執行的Timer跟dismiss BlockView,當然時間到了也是一樣。

這樣就模擬出一個完整的Req/Rep請求了。

再來是重頭戲,也就是Pub/Sub登場,這邊才是ZMQ Socket的精華,所謂的Pub跟Sub就是訂閱模式,ZMQ Socket的channel會一直打資料,想要接到這個channel的資料,我們就必須透過一個subIP跟subTopic去對這個Chananl做訂閱,前著是我們透過Req/Rep請求之後,伺服器會回給我們:

{
    success = 1;
    subIP = XXX.XXX.XX.XXX;
    subTopic = XXXXXXXXXXXXXX;
}

然後我們就把supIP跟subTopic拿出來使用:

subIP = jsondata[@"subIP"];
subTopic = jsondata[@"subTopic"];

前面的Req/Rep都一樣,就不寫了,不一樣的是,這邊成功我們才進行下一步驟,失敗就跟Req/Rep的失敗處理一模一樣,所以成功我們不能把線程砍掉,因為要繼續接著做Sub的動作:

ZMQContext *subctx = [[ZMQContext alloc] initWithIOThreads:1];

ZMQSocket *subrequester = [subctx socketWithType:ZMQ_SUB]; //Socket模式為sub
BOOL subdidConnect = [subrequester connectToEndpoint:subIP]; //這裡放subip
BOOL didSub =  [subrequester subscribe:subTopic]; //這裡放subTopic

if (!subdidConnect) {
    NSLog(@"*** Failed to connect to endpoint [%@].", subIP);
}
if (!didSub) {
    NSLog(@"*** Failed to subing");
}

//用一個無限迴圈來接收Socket傳來的資料   
while (1) {
    NSData *reply = [subrequester receiveDataWithFlags:0];
    NSMutableDictionary *subjsondata = [NSJSONSerialization JSONObjectWithData:reply options:0 error:nil];
    NSLog(@"Received reply %@", subjsondata);
}

至於停止的方法,正常來說是直接關掉執行緒就好,但是我這個案子上有多一個API是主動停止,一樣是在開一條序去做Req/Rep請求,成功後再把兩個線程(Sub線程還有剛剛開的Req/Rep請求)殺掉就可以了。

也就是說我們的流程是,Req/Rep成功後把supIP跟subTopic撈出來,再開啟sub訂閱channel跟接收資料,至於使用Socket刷新UI,使用Notification的內部通知就可以了,記得要回到主線程再更新UI,NSThread停止線程後,請記得一定要 thread = nil; 。

results matching ""

    No results matching ""