我已经实施了有效的解决方案,请参阅下面的评论
您好,感谢您抽出宝贵时间阅读这篇文章!
我正在开发一个应用程序,它连接到广播自己的AdHoc WiFi网络的硬件设备。我能够通过CFNetwork与NSStream的免费桥接连接到设备,发送字节和接收字节。我正在使用相当“事实上”的流开放代码,并且流委托正在报告NSStreamEvents。
在初始化与设备的连接后,我可以看到输入和输出流都打开(NSStreamEventOpenCompleted),并且当硬件设备不断发送“HELLO!”时,inputStream上立即出现BytesAvailable(NSStreamEventHasBytesAvailable)。
在NSStreamEventHasBytesAvailable的case事件中,我从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可以解答我所有的疑问/挫折吗?
再次感谢您抽出宝贵时间阅读这篇文章。我希望我已经清楚地解释了我的问题和我想要的解决方案。如果您有任何疑问,请随时提出,我会尽力回答。
答案 0 :(得分: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提供了有用的知识和输入,我希望我在这里提供的半伪代码(确实可以正常运行!)帮助其他开发人员,因为这是我一直在努力解决的问题。至少一个星期!
答案 1 :(得分:1)
你遇到了与CocoaAsyncSocket相同的问题(尽管这是一个很棒的项目)。 TCP连接正常工作,但只有当另一方通过规则断开连接时才会检测到断开连接。如果线路坏了(关闭设备,超出范围......),您需要一些机制来检测这一点。这是客户的任务。
您正在考虑使用NSTimer
。
有几种方法可以解决这个问题,主要取决于一件事:您的设备是自己发送数据(连接成功后)还是您的应用程序必须请求数据。
但解决方案基本相同。成功连接后,您可以创建可重复的NSTimer。您还需要某种dataAge
变量。此计时器(可称为connectionMonitor
)每次触发时都会增加dataAge
。
如果dataAge
太大(> 5秒),则会破坏连接(也是计时器)并开始全程连接程序。
当然,每次从设备获取数据时都应重置dataAge
。
您还应该处理事件NSStreamEventErrorOccurred
和NSStreamEventEndEncountered
:可能会破坏connectionMonitor
并重新启动连接过程。
您可能知道本教程,但以防万一:iPhone Network Programming