2012-06-02 40 views
3

我實現了一個工作方案,請參見下面監視器連接狀態和處理丟棄

你好我的評論,感謝您抽出時間來閱讀這篇文章!

我正在開發一個應用程序,它連接到一個廣播自己的AdHoc WiFi網絡的硬件設備。我可以連接到設備,發送字節,並通過CFNetwork與NSStream進行免費橋接來接收字節。我正在使用相當「事實上」的流開放代碼,並且流委託正在報告NSStreamEvents。我可以看到輸入和輸出流都打開(NSStreamEventOpenCompleted),並且隨着硬件設備不停地發送「HELLO!」,InputStream上立即有BytesAvailable(NSStreamEventHasBytesAvailable)。

在爲NSStreamEventHasBytesAvailable的情況時,我讀了從InputStream數據和日誌它像這樣:

case NSStreamEventHasBytesAvailable: 
     NSLog(@"CASE LOG -- NSStreamEventHasBytesAvailable"); 
     uint8_t buffer[256]; 
     int len; 

     while ([inputStream hasBytesAvailable]) { 
      //NSLog(@"LOG -- inputStream hasBytesAvailable"); 
      len = [inputStream read:buffer maxLength:sizeof(buffer)]; 
      if (len > 0) { 

       NSLog(@"Length of inputStream Bytes -- %i",len); 
       NSString *output = [[NSString alloc] initWithBytes:buffer length:len encoding:NSASCIIStringEncoding]; 

       // This global boolean affects the rest of the apps functionality. Buttons are not able to send bytes to the hardware device if this boolean is FALSE.      
       deviceIsConnected = true; 

       // If buttons to send bytes are disabled due to lack of network connection on appLaunch, then go ahead and show them, allowing the user to send bytes to the hardware device 
       if(buttonsAreDisabled == true) 
       { 
        [ self setButtonVisibility:true ]; 

        // Reset the status of the "No Connection" Alert 
        connectionAlertShown = false; 
       } 

       // Log the incoming data 
       if (nil != output) { 
        NSLog(@"LOG -- device said: %@", output); 
       } 
      } 
     } 
    break; 

正如預期的那樣,我有絡繹不絕的「LOG - 設備說:XXXX」,而我的設備已連接。但是,如果我斷開設備與電源的連接,則不會收到任何類型的流事件;日誌記錄只是一起停止。

我試圖通過在我的viewDidLoad中啓動一個backgroundTimer來解決這個問題,每0.1秒嘗試從inputStream中讀取。如果無法讀取,則布爾型deviceIsConnected設置爲FALSE,並且會顯示一條警報,通知用戶它們與設備的連接已斷開。

這種方法已被證明是相當不可靠的,也是一種看起來很簡單的檢測套接字連接關閉的簡單任務。如果我理解正確,NSStream類基本上是BSD套接字體系結構之上的「中間人」或抽象層。

斷開硬件設備與其電源的連接正在模擬出設備板載WiFi芯片的範圍。這不是一個「真實世界」測試,就好像你是身體遠離設備,你不會突然失去聯繫;相反,inputStream接收的數據會慢慢惡化,從而導致「Network Alert」彈出窗口在設備在「連接」和「未連接」之間跳轉時持續閃爍。

我想實現某種KeepAlive處理程序,但我缺乏iPhone/iOS/BSD套接字的經驗嚴重阻礙了我。如果你們中的任何一個人都可以提供一個方法的基本示例(可能運行在一個定時器上,我認爲我在那裏正確的道路上!),它可以檢測到套接字變得不可用並繼續嘗試重新建立連接,I會永遠感激。我不知疲倦地搜索了Google,發現了一些有前途的想法,但還沒有能夠成功實現它們中的任何一個。

CocoaASyncSocket可以解答我所有的問題/挫折嗎?

再次感謝您花時間閱讀本文。我希望我已經提供了我的問題和我想要的解決方案的明確解釋。如果您有任何問題,請隨時詢問,我會盡我所能來回答。

+0

一個問題:你是否也控制了hw-device的行爲(你是否在爲它寫一個固件)? –

+0

@rokjarc是的,硬件設備是在Arduino平臺上開發的。硬件邏輯非常簡單:只需從遠程設備(iPhone)接收一個字節值,並相應地對12v信號作出反應。 –

回答

2

我前面解釋的理論(見註釋)確實奏效。我現在可以使用以下邏輯成功監控設備連接狀態(請理解整個應用程序中存在以下代碼塊)。我能夠確定設備變得不可用的確切時刻;無論是因爲缺乏WiFi連接還是設備失電。

// Define two socket objects, a "Main" socket and "Observer" socket 

asyncSocketMain = [[GCDAsyncSocket alloc] initWithDelegate:self delegateQueue:dispatch_get_main_queue()]; 
dispatch_queue_t secondaryQueue = dispatch_get_current_queue(); 
asyncSocketObserver = [[GCDAsyncSocket alloc] initWithDelegate:self delegateQueue:secondaryQueue]; 

// On application launch, attempt to open asyncSocketMain 
[asyncSocketMain connectToHost:host onPort:port withTimeout: 2.0 error:&error] 

// Determine which socket object is connecting in the didConnectToHost method 
- (void)socket:(GCDAsyncSocket *)sock didConnectToHost:(NSString *)host port:(UInt16)port 
{ 
NSString *currentSocketId = (NSString *)sock; 

// Determine Which Socket Object is Connecting... 
if(currentSocketId == (NSString *)asyncSocketMain) 
{ 
    NSLog(@"Main Socket has Connected!"); 
    connectionIsOpening = false; // Allow for Future Reconnect Attempts 
    deviceIsConnected = true; // Allow Connection Monitoring 

    // If the Main Socket has been attempting to reconnect, stop doing that! 
    if(reconnectTimer) 
    { 
     [ reconnectTimer invalidate ]; // Stop the reconnectTimer 
     reconnectTimer = nil;   // And also set its value to nil 
    } 
    [ self setupMonitorTimer ]; // Begin Monitoring the Connection 
}else{ 
    if(currentSocketId == (NSString *)asyncSocketObserver) 
    { 
     NSLog(@"Observer Socket attempting connection! Socket: %@", sock); 
    }else{ 
     NSLog(@"ALERT ALERT -- UNKNOWN SOCKET CONNECTING!!!"); 
    } 
} 
} // close void 

現在,當觀察者套接字嘗試連接時,將引發錯誤。這是我能夠確定當前連接的方式。如果asyncSocketMain套接字已連接,觀察者將始終拋出錯誤代碼7。如果asyncSocketObserver嘗試連接時超時,則表示設備已關機,超出範圍或以其他方式不可用(例如用戶手機未連接到正確的WiFi網絡)。在這種情況下,應該停止所有「監控」,並啓動asyncSocketMain的計時器以嘗試重新連接。

- (void)socketDidDisconnect:(GCDAsyncSocket *)sock withError:(NSError *)err 
{ 


NSString *connectingSocket = (NSString *)sock; 

// Figure out hte Error Code Info 
NSError * error; 
error = err; 
NSInteger errorNum = error.code; 


if(connectingSocket == (NSString *)asyncSocketMain) 
{ 
    // This may occur if the app is opened when the device is out of range 
    NSLog(@"The MAIN SOCKET Encountered an Error while Connecting! [ CODE: %d ]", errorNum); 
    [ asyncSocketMain disconnect ]; // Disconnect the Main Socket to allow for reconnects 
    connectionIsOpening = false; // Allow Main Connection Attempts 
    deviceIsConnected = false;  // Device is NOT CONNECTED -- Do NOT RUN MONITORING 
    if(!reconnectTimer) 
    { 
     NSLog(@"Starting the reconnectTimer"); 
     [ self setupReconnectTimer ]; // Start attempting to reconnect 
    }else{ 
     NSLog(@"Reconnect Timer is Already Running!"); 
    } 
} 
else 
if(connectingSocket == (NSString *)asyncSocketObserver) 
{ 
    switch (errorNum) 
    { 
     case 1: 
      // Not much to do here... 
      NSLog(@"OBSERVER ERROR - There is already a socket attempting to connect!"); 
      break; 

     case 2: 
      // Not much to do here... 
      NSLog(@"OBSERVER ERROR - Event 2"); 
      break; 

     case 3: 
      // Time Out -- The device is out of range. Halt observer connection attempts, disconnect the main 
      // socket object, then proceed to attempt to reconnect with the main socket. 
      NSLog(@"OBSERVER ERROR - Connected Timed out -- Device not available!!!!"); 

      // The Observer Socket Timed out -- It's time to start reconnecting 
      connectionIsOpening = false; // Allow Main Connection Attempts 
      deviceIsConnected = false; // Device is NOT CONNECTED - DO NOT RUN MONITORING and ALLOW CONNECTION ATTEMPTS 
      if(monitorTimer) 
      { 
       // Stop trying to reconnect with the observer socket, thus allowing the Main socket to connect 
       NSLog(@"Stopping the Monitoring Method..."); 
       [monitorTimer invalidate]; 
       monitorTimer = nil; 
      }else{ 
       NSLog(@"Connection Monitoring already halted!"); 
      } 

      // If the reconnectTimer is not running (it shouldnt be, otherwise something is wrong) then go ahead and run it 
      // This will attempt to reconnect asyncSocketMain 
      if(!reconnectTimer) 
      { 
       NSLog(@"Starting the reconnectTimer"); 
       [ asyncSocketMain disconnect ]; // Deallocate the main socket to allow for reconnects 
       [ self setupReconnectTimer ]; 
      }else{ 
       NSLog(@"Reconnection Attempts are already happening! [ reconnectTimer: %@ ]",reconnectTimer); 
      } 

      break; 

     case 7: 
      NSLog(@"OBSERVER ERROR - The Main Socket is Already Connected!"); 
      break; 
    } 
} 
else{ 
    NSLog(@"An Unknown Socket Connection Encountered and Error..."); 
} 
} // end void 

僅供參考,我將所有數據寫在asyncSocketMain上。 asyncSocketObserver對象總是試圖連接一個定時器,只要deviceIsConnected = TRUE

這可能不是監控連接的最優雅的方式,但確實有效。一旦我斷開與我的設備的電源,asyncSocketObserver就超時,然後(按照代碼)停止所有「連接監控」,並生成一個「重新連接」計時器,一旦建立連接就會立即失效。

再次感謝@rokjarc提供的有用的知識和輸入,我希望我在這裏提供的半僞代碼(它確實按預期運行!)幫助其他開發人員,因爲這是我一直在努力的至少一週!

+0

請注意,只有當您連接的主機拒絕同一端口上的後續連接時,此解決方案纔會起作用。否則,您需要實施更多的「心跳」解決方案,您需要發送數據並期望返回某些內容(例如,IRC客戶端使用「ping」和「pong」以保留活動消息),以便您可以確保連接正常運行。 –

1

你會遇到與CocoaAsyncSocket相同的問題(雖然它是一個很棒的項目)。TCP連接正常工作,但只有在另一方斷開「按規則」時纔會檢測到斷開連接。如果一條線路損壞(關閉設備,超出範圍...),您需要一些機制來檢測這一點。這是客戶的任務。

您正在思考如何使用NSTimer

有幾種方法可以解決這個問題,主要取決於一件事情:您的設備是單獨發送數據(連接成功後)還是您的應用程序必須請求數據。

但解決方案基本相同。成功連接後,您創建一個可重複的NSTimer。您還需要某種dataAge變量。這個計時器(可以稱爲connectionMonitor)在每次啓動時增加dataAge

如果dataAge太大(> 5s),則破壞連接(也是定時器)並開始連接過程。

當您從設備獲取數據時,應該重置dataAge

您還應該處理事件NSStreamEventErrorOccurredNSStreamEventEndEncountered:可能會破壞connectionMonitor並重新啓動連接過程。

你可能知道這個教程,但以防萬一:iPhone Network Programming

+0

感謝您輸入@rokjarc。我正在一個我已經實現CocoaASyncSocket的分支上工作,並且相信我可能找到了一個解決方案。我定義了兩個'GCDASyncSocket'對象,其中一個我聲明爲'MainSocket'(數據將被寫入),另一個作爲'Ob​​serverSocket'。一旦'MainSocket'建立了一個連接,'ObserverSocket'開始連接一個重複的'NSTimer',當連接'MainSocket'時,'ObserverSocket'連接嘗試的結果將返回'Code 7'已在使用中)。代碼3 =超時,這意味着我重新啓動連接過程。 –

+0

爲了記錄,我正在從設備接收數據,但是當使用'NSStream'進行測試時,我遇到了使用'dataAge'實現的輕度不可靠結果。鑑於硬件設備的性質及其目的,如果設備不可達,則必須立即切斷連接。 通過使用'NSStream' hasBytesAvailable'觸發'dataAge'方法,當站在硬件設備的WiFi範圍邊緣附近時,我遇到了奇怪的行爲;即連接在「連接」和「未連接」之間來回切換......可能是我的邏輯失敗。 –

+0

這可能是一個實現問題。我有一個市場上具有類似連接行爲的設備(iPad應用程序連接到基於MRF24WB0的WiFi AdHoc設備),該算法工作起來很麻煩。在你的地方,我會嘗試使用一個套接字和一個計時器。 ObserverSocket似乎不需要的開銷。是的,當處於WiFi範圍的邊緣時,您將遇到更高的延遲,並可能導致一些數據粘連在一起。 –