3

我已经实施了一个可行的解决方案,请在下面查看我的评论

您好,感谢您抽出宝贵时间阅读这篇文章!

我正在开发一个应用程序,该应用程序连接到广播自己的 AdHoc WiFi 网络的硬件设备。我能够通过 CFNetwork 与 NSStream 的免费桥接连接到设备、发送字节和接收字节。我正在使用相当“事实上”的流打开代码,并且流委托正在报告 NSStreamEvents。

初始化与设备的连接后,我可以看到输入和输出流都打开(NSStreamEventOpenCompleted),并且由于硬件设备不断发送“HELLO!”,因此输入流上立即有 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 接收到的数据会慢慢恶化,从而导致“网络警报”弹出窗口在设备在“已连接”和“未连接”之间跳转时不断闪烁。

我想实现某种 KeepAlive 处理程序,但我缺乏 iPhone / iOS / BSD 套接字的经验严重阻碍了我。如果你们中的任何一个出色的人可以提供一个方法的基本示例(可能在计时器上运行,我认为我在正确的道路上!)可以检测到套接字变得不可用并继续尝试重新建立连接,我将永远感激不尽。我不知疲倦地搜索了谷歌,发现了一些有希望的想法,但都未能成功实施。

CocoaASyncSocket 能否成为我所有问题/挫折的答案?

再次感谢您花时间阅读这篇文章。我希望我已经清楚地解释了我的问题和我想要的解决方案。如果您有任何问题,请随时提出,我会尽力回答。

4

2 回答 2

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 提供的有用知识和输入,我希望我在这里提供的半伪代码(确实按预期运行!)帮助其他一些开发人员,因为这是我至少为之苦苦挣扎的事情一周!

于 2012-06-03T20:39:15.923 回答
1

CocoaAsyncSocket 也会有同样的问题(尽管它是一个很棒的项目)。TCP 连接正常工作,但只有在对方“按规则”断开连接时才会检测到断开连接。如果线路中断(关闭设备,超出范围......),您需要一些机制来检测这一点。这是客户的任务。

您正在考虑使用NSTimer.

有几种方法可以解决这个问题,主要取决于一件事:您的设备是自己发送数据(成功连接后)还是您的应用程序必须请求数据。

但解决方案基本相同。成功连接后,您将创建一个可重复的 NSTimer。您还需要某种dataAge变量。这个计时器(可以称为connectionMonitordataAge每次触发时都会增加。

如果dataAge太大(> 5s),则破坏连接(也是计时器)并重新开始连接过程。

当然,dataAge每次从设备获取数据时都应该重置。

您还应该处理事件NSStreamEventErrorOccurredNSStreamEventEndEncountered:可能会破坏connectionMonitor和重新启动连接过程。

您可能知道本教程,但以防万一:iPhone 网络编程

于 2012-06-02T19:03:10.783 回答