In my application I connect to a server using
- (void)connectToServerUsingCFStream:(NSString *) urlStr portNo: (uint) portNo
This function is called by another method
- (void)connectToServer:(NSString *)serverName onPort:(int)portNo
{
[self connectToServerUsingCFStream:serverName portNo:portNo];
while (!((iStream.streamStatus == 2) || (oStream.streamStatus == 2))) {
continue;
}
NSLog(#"Streams connected");
[self sendLoginRequest];
}
Now I want to know wether there is an easy possibility to check if my connection request is timed out (maybe with a certain time value?). Is there a way to handle this in my while loop or should I use something different?
Thanks in advance,
Bautzi
I have no idea how you exactly implement the connection, but here I have some connection codes from the XMPPFramework , as the code comments:
/**
* XMPPReconnect handles automatically reconnecting to the xmpp server due to accidental disconnections.
* That is, a disconnection that is not the result of calling disconnect on the xmpp stream.
*
* Accidental disconnections may happen for a variety of reasons.
* The most common are general connectivity issues such as disconnection from a WiFi access point.
*
* However, there are several of issues that occasionaly occur.
* There are some routers on the market that disconnect TCP streams after a period of inactivity.
* In addition to this, there have been iPhone revisions where the OS networking stack would pull the same crap.
* These issue have been largely overcome due to the keepalive implementation in XMPPStream.
*
* Regardless of how the disconnect happens, the XMPPReconnect class can help to automatically re-establish
* the xmpp stream so as to have minimum impact on the user (and hopefully they don't even notice).
*
* Once a stream has been opened and authenticated, this class will detect any accidental disconnections.
* If one occurs, an attempt will be made to automatically reconnect after a short delay.
* This delay is configurable via the reconnectDelay property.
* At the same time the class will begin monitoring the network for reachability changes.
* When the reachability of the xmpp host has changed, a reconnect may be tried again.
* In addition to all this, a timer may optionally be used to attempt a reconnect periodically.
* The timer is started if the initial reconnect fails.
* This reconnect timer is fully configurable (may be enabled/disabled, and it's timeout may be changed).
*
* In all cases, prior to attempting a reconnect,
* this class will invoke the shouldAttemptAutoReconnect delegate method.
* The delegate may use this opportunity to optionally decline the auto reconnect attempt.
*
* Auto reconnect may be disabled at any time via the autoReconnect property.
*
* Note that auto reconnect will only occur for a stream that has been opened and authenticated.
* So it will do nothing, for example, if there is no internet connectivity when your application
* first launches, and the xmpp stream is unable to connect to the host.
* In cases such as this it may be desireable to start monitoring the network for reachability changes.
* This way when internet connectivity is restored, one can immediately connect the xmpp stream.
* This is possible via the manualStart method,
* which will trigger the class into action just as if an accidental disconnect occurred.
**/
I don't know if this XMPPReconect class meets your demand.
Related
In the <GCKDeviceManagerDelegate> Protocol I see two very similar methods:
/**
* Called when the connection to the device has been terminated. It is safe to release the
* GCKDeviceManager object from within this callback.
*
* #param deviceManager The device manager.
* #param error The error that caused the disconnection; nil if there was no error (e.g. intentional
* disconnection).
*/
- (void)deviceManager:(GCKDeviceManager *)deviceManager
didDisconnectWithError:(NSError *)error;
and
/**
* Called when disconnected from the current application.
*
* #param deviceManager The device manager.
* #param error The error that caused the disconnect, or <code>nil</code> if this was a normal
* disconnect.
*/
- (void)deviceManager:(GCKDeviceManager *)deviceManager
didDisconnectFromApplicationWithError:(NSError *)error;
Is deviceManager:didDisconnectWithError: called if the Chromecast receiver is disconnected or loses connection to the application, and deviceManager:didDisconnectFromApplicationWithError: called when the application tries to disconnect from the Chromecast receiver?
What are the use-cases when each delegate method would be called?
A sender application can connect to a cast device and upon successful connection can launch a receiver application on the cast device. So "connecting to a device" and "running an application" are two separate actions, so are "stopping the receiver application" and "disconnecting" from the cast device. As such, there are those two separate methods.
I am using Reachability class from Apple to detect network events that have impact in the functionality of my app. It is a voip app that uses setKeepAliveTimeout, so every ~10 minutes wakes up reads the network status and decides if the connection should be refreshed.
BOOL res = [app setKeepAliveTimeout:600 handler:^{
[[WIFI instance] isWifiConnected];
[[AClass an_instance] refresh];
}
}];
So every 10 minutes the isWifiConnected is called and the app reads the network status again.
- (BOOL) isWifiConnected {
self.wifiReach = [Reachability reachabilityForLocalWiFi];
NetworkStatus wifiStatus = [self.wifiReach currentReachabilityStatus];
switch (wifiStatus) {
case NotReachable: {
m_wifiConnected = NO;
LOG(#"NetStatus:NotReachable");
break;
}
case ReachableViaWiFi: {
m_wifiConnected = YES;
m_wwanConnected = NO;
LOG(#"NetStatus:ReachableViaWiFi");
break;
}
}
return m_wifiConnected;
}
Although I have WiFi in the device the call returns false, ie no WiFi, and moreover NotReachable for the net status.
However after a very short time interval the reachability callback is called again and the the wifi seems connected. However i have already fired an event due to the error value and the app closes the connection with the server believing that there is no wi-fi.
Doing some research it I found this in the Readme file of the Reachability.m file (provided from Apple)
By default, the application uses www.apple.com for its remote host. You can change the host it uses in APLViewController.m by modifying the value of the remoteHostName variable in -viewDidLoad.
IMPORTANT: Reachability must use DNS to resolve the host name before
it can determine the Reachability of that host, and this may take time
on certain network connections. Because of this, the API will return
NotReachable until name resolution has completed. This delay may be
visible in the interface on some networks
.
Could it this be the problem? The delay in the dns lookup? Or do I need to enhance my code as well?
When I initialize the app I call this
self.hostReach = [Reachability reachabilityWithHostName: #"www.apple.com"];
If I use an IP address like this is correct?
self.hostReach = [Reachability reachabilityWithHostName: #"1.2.3.4"];
Is it safe to use a public IP? eg "17.178.96.59" is the result of an nslookup for apple.com
There is a method in the Reachability class that seems to be used from the Apple's demo.
- (BOOL)connectionRequired
{
NSAssert(_reachabilityRef != NULL, #"connectionRequired called with NULL reachabilityRef");
SCNetworkReachabilityFlags flags;
if (SCNetworkReachabilityGetFlags(_reachabilityRef, &flags))
{
return (flags & kSCNetworkReachabilityFlagsConnectionRequired);
}
return NO;
}
Why the connectionRequired is needed? Can be used to resolved the problem?
To preserve battery charge, iOS will close down the networking hardware when it's not being actively used. This means turning off the WiFi and cellular radios. In this situation, Reachability isn't going to be able to report a complete result, because it can't check the situation without turning everything back on again. That is what kSCNetworkReachabilityFlagsConnectionRequired is for -- to tell you that you need to make a connection to wake the hardware back up.
What you are seeing is probably something waking up when the phone is unlocked (your app or some other app with background permissions) and so everything wakes up, you see an error immediately, but then WiFi connects and you are connected again.
You need to handle Reachability as if it can tell you "definitely reachable" or "definitely not reachable" but also "situation unknown". You need to decide what you're going to do in the unknown situation. If you make a network connection immediately then you will drain the battery faster than normal. An alternative might be just to wait for the network to be woken up for some other reason. That's really up to you.
Reachability should be created with a host name, not an explicit address. The whole point of the DNSsystem is that addresses change for hosts sometimes. Linking directly to a name server should provide some security in this regard, but that isn't how it's supposed to work.
Reachability is often a best guess, not a hard and fast. The only way to be sure is to actually try. Connection required is related to this, because it's the device saying 'everything looks ok, I just haven't tried to connect for real'.
So, arguably you should fire the keep alive without checking the reachability status and use the result of the request to decide if there's an error or not. If you need to be sure, send an actual request and review the actual result.
I've recently posted a question about reducing the timeout of an FTP connection (click here to see it if you want).
Now, I've been asked to post a more specific question, focusing on the component we're using for the FTP download.
We're using Nico Kreipke's FTPManager (click here to go to its GitHub).
What we're trying to implement is to download data from an FTP address, and if it fails we'll fallback to use an HTTPS web server.
When the FTP address we give isn't available, it takes about one minute to timeout.
The question is, how can I reduce that timeout?
Best regards,
Tiago
Some More Info
I forgot to say, the FTP connection is done with an IP (local network).
Johan's Tip
I added a property to FTPManager, a double named timeout.
Then I've overridden the accessor of serverReadStream, a property used throughout FTPManager to hold the read stream, so that it would configure the timeout interval for all requests.
- (NSInputStream *)serverReadStream
{
return _serverReadStream;
}
- (void)setServerReadStream:(NSInputStream *)serverReadStream
{
if ((_serverReadStream = serverReadStream)) {
CFNumberRef number = CFNumberCreate(kCFAllocatorDefault, kCFNumberDoubleType, &_timeout);
CFReadStreamSetProperty((__bridge CFReadStreamRef)(_serverReadStream), _kCFStreamPropertyReadTimeout, number);
CFRelease(number);
}
}
_kCFStreamPropertyReadTimeout is defined by:
#define _kCFStreamPropertyReadTimeout CFSTR("_kCFStreamPropertyReadTimeout")
However, it still takes about one minute to timeout. I set the timeout before connecting to the FTP address, right after creating ftpManager. The code I use to set the timeout follows:
FTPManager *ftpManager = [[FTPManager alloc] init];
[ftpManager setTimeout:10];
Have you tried something like using performSelector:withObject:afterDelay: with a custom method that checks whether the connection has already been established and data could be received, otherwise calls [ftpManager abort]?
Not a real connection timeout and seems kinda dirty, but should do the job.
I think it can be done by simply setting a property of CFReadStream. So you probably need to subclass the FTPManager.
The property is called _kCFStreamPropertyReadTimeout.
#define _kCFStreamPropertyReadTimeout CFSTR("_kCFStreamPropertyReadTimeout")
Then add this to appropriate method.
double timeout = 30;
CFReadStreamRef readStream = CFReadStreamCreateWithFTPURL(NULL, (__bridge CFURLRef)[[server.destination ftpURLForPort:server.port] URLByAppendingPathComponent:fileName]);
CFNumberRef number = CFNumberCreate(kCFAllocatorDefault, kCFNumberDoubleType, &timeout);
CFReadStreamSetProperty(readStream, _kCFStreamPropertyReadTimeout, number);
CFRelease(num);
I looked into GCDAsyncSocket.m at the code that handles read timeout. If I don't extend the timeout, it seems that socket got closed and there is no option to the socket alive keep. I can't use infinite timeout (timeout = -1) because I still need to know when it is timed out, but also doesn't want it to disconnect. I'm not sure there is a reason behind this. Does anyone know?
- (void)doReadTimeoutWithExtension:(NSTimeInterval)timeoutExtension
{
if (currentRead)
{
if (timeoutExtension > 0.0)
{
currentRead->timeout += timeoutExtension;
// Reschedule the timer
dispatch_time_t tt = dispatch_time(DISPATCH_TIME_NOW, (timeoutExtension * NSEC_PER_SEC));
dispatch_source_set_timer(readTimer, tt, DISPATCH_TIME_FOREVER, 0);
// Unpause reads, and continue
flags &= ~kReadsPaused;
[self doReadData];
}
else
{
LogVerbose(#"ReadTimeout");
[self closeWithError:[self readTimeoutError]];
}
}
}
FYI, there is a pull request at https://github.com/robbiehanson/CocoaAsyncSocket/pull/126 that adds this keep-alive feature but it is not pulled yet.
I am the original author of AsyncSocket, and I can tell you why I did it that way: there are too many ways for protocols to handle timeouts. So I implemented a "hard" timeout and left "soft" timeouts up to the application author.
The usual way to do a "soft" timeout is with an NSTimer or dispatch_after. Set one of those up, and when the timer fires, do whatever you need to do. Meanwhile, use an infinite timeout on the actual readData call. Note that infinite timeouts aren't actually infinite. The OS will still time out after, say, 10 minutes without successfully reading. If you really want to keep the connection alive forever, you might be able to set a socket option.
am connectting the MQ with below code. I am able connected to MQ successfully. My case is i place the messages to MQ every 1 min once. After disconnecting the cable i get a ResonCode error but IsConnected property still show true. Is this is the right way to check if the connection is still connected ? Or there any best pratcices around that.
I would like to open the connection when applicaiton is started keep it open for ever.
public static MQQueueManager ConnectMQ()
{
if ((queueManager == null) || (!queueManager.IsConnected)||(queueManager.ReasonCode == 2009))
{
queueManager = new MQQueueManager();
}
return queueManager;
}
The behavior of the WMQ client connection is that when idle it will appear to be connected until an API call fails or the connection times out. So isConnected() will likely report true until a get, put or inquire call is attempted and fails, at which point QMgr will then report disconnected.
The other thing to consider here is that 2009 is not the only code you might get. It happens to be the one you get when the connection is severed but there are connection codes for QMgr shutting down, channel shutting down, and a variety of resource and other errors.
Typically for a requirement to maintain a constant connection you would want to wrap the connect and message processing loop inside a try/catch block nested inside a while statement. When you catch an exception other than an intentional exit, close the objects and QMgr, sleep at least 5 seconds, then loop around to the top of the while. The sleep is crucial because if you get caught in a tight reconnect loop and throw hundreds of connection attempts at the QMgr, you can bring even a mainframe QMgr to its knees.
An alternative is to use a v7 WMQ client and QMgr. With this combination, automatic reconnection is configurable as a channel configuration.