2012-06-02 36 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范围的边缘时,您将遇到更高的延迟,并可能导致一些数据粘连在一起。 –