I am using the socket.io framework for a chat module in Objective-C code base. The connection however loses very frequently, sometimes right after the connection is made, it's disconnected. I have looked for background app and incoming call scenarios as answered in SO, but here it happens even if app is active and chat is in process.
Here is how I make the connection:
NSURL* url = [[NSURL alloc] initWithString:#"http://domainAddress:port"];
self.socket = [[SocketIOClient alloc] initWithSocketURL: url config: #{#"connectParams": #{#"user_id": [NSNumber numberWithInt:user_id]}}];
[self.socket setReconnects:YES];
I used the setReconnects: method as initially the socket was not reconnecting itself.
Please let me know how to resolve the issue and how can I debug more further.
Related
I am developing an app for iOS with Swift 3, this application search with the Bonjour service some robots in the local network which use a specific service, for example robot.local and show them in a list. The Bonjour service gives me the domain of the device. This is a example of the domains searched.
Ex.:
robot1.local
robot2.local
The next step, is when the user click an element of the list. This action start a connection by web sockets with the device and connect it for control it with the Iphone. I am using a library called RBManager which use RocketSocket library for connect. This library helps me to connect to RosBridge.
I use this code for connect:
NSURLRequest * request = [NSURLRequest requestWithURL:[NSURL URLWithString:#"wss://192.168.0.100:9090"]];
self.socket = [[SRWebSocket alloc] initWithURLRequest:request];
self.socket.delegate = self;
[self.socket open];
The problem is when I am install the app by Xcode I have not any problem but when I am install the app by an ipa file or by TestFlight the connection is rejected and shows this error:
managerDidFailWithError Optional(ErrorDomain=NSOSTatusErrorDomain
Code=-9807 "(null)" UserInfo={_kCFStreamErrorDomainKey=3,
_kCFStreamErrorCodeKey=-9807})
I found this issue in the library but is not the solution that I need.
I am deactive ATS in the info.plist but I not know how to solve this error. Could anyone help me?
I found the problem. The problem was that my RosBridge backend run with TLS and I didn't implement it.
The solution is implement the authentication in the client and all works :D
I have been struggling with an issue where NSURLConnection calls instantly fail. The device needs to be rebooted entirely or Flight Mode needs to be toggled on/off to resolve the problem. Restarting the app (swipe up) alone does not help.
Some facts:
-All URLs are HTTPS, TLS 1.2 compatible with Forward Secrecy. There are no issues with ATS and iOS 9. The error has been present since iOS 7 and remains on 9.2.
-No third party frameworks are used by the app. I use only native NSURLConnection calls that always work, except for when this odd situation occurs.
-No infrastructure/network issues - other devices on same networks (same WiFi for instance) work in the same app at the same time. Going to/from 3G/Wifi makes no difference.
-I always implement willCacheResponse to return nil.
-The service is hosted on AWS Elastic Beanstalk, so some suggested that it might be a DNS caching issue in case of IP address changes - this seems unlikely to me and should trigger multiple errors at once on different devices, which I have never seen.
-The method called is didFailWithError, instantaneously, as if there were no Internet connection on the device at all - all other apps work, however.
-The website that hosts the API used by the app can be browsed with no problems at all times. The website actually makes the same requests to fetch data.
The error code returned is -1003, kCFURLErrorCannotFindHost. I've been following a thread on Git dealing with the same issue to no avail. https://github.com/AFNetworking/AFNetworking/issues/967
I tried using NSURLRequestReloadIgnoringCacheData for all my requests, but that did not help.
With this information, will anyone care to venture a guess what I might be doing wrong? I added the bounty because I have no idea how to approach this problem - especially because it's so inconsistent. And it is definitely not a legitimate error (that is, that the domain could not be found), as the service is operating fine while this happens on random clients.
I create my request with a static method that looks like this. It's been stripped of some non-public info, but basically it just performs a POST request with JSON data. [Controller getSQLHost] just returns a URL - the base domain.
+(NSURLConnection*)initiatePOSTwithJSONDictionary:(NSDictionary*)dictionary toURL:(NSString*)urllocation withDelegate:delegate {
NSMutableDictionary *connectionDictionary = [[NSMutableDictionary alloc] init];
if (dictionary) {
[connectionDictionary setObject:dictionary forKey:#"input"];
}
NSData *jsonData = [NSJSONSerialization dataWithJSONObject:connectionDictionary options:kNilOptions error:nil];
NSURL *url = [NSURL URLWithString:[[Controller getSQLHost] stringByAppendingString:urllocation]];
NSString *postLength = [NSString stringWithFormat:#"%i", (int)[jsonData length]];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url cachePolicy:NSURLRequestReloadIgnoringCacheData timeoutInterval:30.0];
[request setHTTPMethod:#"POST"];
[request setValue:postLength forHTTPHeaderField:#"Content-Length"];
[request setHTTPBody:jsonData];
return [[NSURLConnection alloc] initWithRequest:request delegate:delegate];
}
Does you delegate implement connectionShouldUseCredentialStorage ? (or respond with YES)
I think the device's keychain is used when this method returns yes which may explain the persisting failure beyond the life time of the running application and why rebooting or otherwise resetting network connectivity "fixes" it. If an authentication failure has been recognized once, it may linger in the key chain for a little while which would then respond immediately without actually going to the server.
What would cause the authentication to register as a failure in the keychain in the first place may depend on a variety of factors. It could be as simple as a typo in the saved password or more convoluted such as some certificate expiration preventing the SSL layer from establishing a secure link.
You're creating NSURLConnections on the current runloop. How many are active at any one time? Have you considered using an NSOperationQueue so that you don't get bitten by load bugs?
Is your delegate thread-safe? If not, that could explain the sporadic-ness of the bug.
You say you don't see the problem often, but others do. Can you borrow their devices and maybe even them and their usage patterns and thus get to see the problem more often?
Requests don't seem to be sent when in 3G. The code is called and it returns to the callback with a -1001 error and the following message: "(The request timed out.)"
Protocol is HTTP, not HTTPs.
Code works fine in Wi-Fi.
Wireshark on the server does not show any incoming data when app is in 3G (but data is received when in Wi-Fi).
After network inspection in Instruments: request is shown but no data / packets out.
Code Below:
self.request = [[NSMutableURLRequest alloc] initWithURL:[NSURL URLWithString:self.route]];
[self.request setHTTPMethod:self.httpMethod];
self.request.timeoutInterval = XX.f;
[NSURLConnection sendAsynchronousRequest:self.request
queue:queue
completionHandler:^(NSURLResponse *response, NSData *data, NSError *error) {
// Stuff actually getting called but with -1001 error code (only when NOT in Wi-Fi)
}
It seems that OSX 10.10 Beta can corrupt the network privileges of an app when installing from XCode.
The issue can be worked-around by completely uninstalling the app and rebooting the phone (pressing home and power button simultaneously).
The error code you get (-1001) resolves to NSURLErrorTimedOut, which means your request timed out. I recommend you check your firewall (at the server-end). Is your server accessible from your phone? Is port 80 accessible from your phone? Note that the IP address of your phone when you're using WiFi differs from the IP address you get using 3G.
We´re developing a HTTP-streaming iOS app that requires us to receive playlists from a secured site. This site requires us to authenticate using a self signed SSL certificate.
We read the credentials from a .p12 file before we use NSURLConnection with a delegate to react to the authorization challenge.
- (void)connection:(NSURLConnection *)connection didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge
{
[[challenge sender] useCredential: self.credentials forAuthenticationChallenge:challenge];
}
- (BOOL)connection:(NSURLConnection *)connection canAuthenticateAgainstProtectionSpace:(NSURLProtectionSpace *)protectionSpace
{
return YES;
}
By doing this initial connection to the URL where we´re getting the .m3u8 playlist we´re able to play back the playlist using AVPlayer. The problem is that this method only works in the simulator.
NOTE: We´re able to download the playlist using the NSURLConnection on device. This must mean that the AVPlayer somehow can´t continue using the trust established during this initial connection.
We have also tried adding the credentials to the [NSURLCredentialStorage sharedCredentialStorage] without any luck.
Below follows our shotgun approach for that:
NSURLProtectionSpace *protectionSpace = [[NSURLProtectionSpace alloc]
initWithHost:host
port:443
protocol:#"https"
realm:nil
authenticationMethod:NSURLAuthenticationMethodClientCertificate];
[[NSURLCredentialStorage sharedCredentialStorage] setDefaultCredential:creds
forProtectionSpace:protectionSpace];
NSURLProtectionSpace *protectionSpace2 = [[NSURLProtectionSpace alloc]
initWithHost:host
port:443
protocol:#"https"
realm:nil
authenticationMethod:NSURLAuthenticationMethodServerTrust];
[[NSURLCredentialStorage sharedCredentialStorage] setDefaultCredential:creds
forProtectionSpace:protectionSpace2];
EDIT: According to this question: the above method doesn´t work with certificates.
Any hint to why it doesn´t work on device, or an alternate solution is welcome!
From iOS 6 onwards AVAssetResourceLoader can be used for retrieving an HTTPS secured playlist or key file.
An AVAssetResourceLoader object mediates resource requests from an AVURLAsset object with a delegate object that you provide. When a request arrives, the resource loader asks your delegate if it is able to handle the request and reports the results back to the asset.
Please find the sample code below.
// AVURLAsset + Loader
AVURLAsset *asset = [[AVURLAsset alloc] initWithURL:url options:nil];
AVPlayerItem *playerItem = [AVPlayerItem playerItemWithAsset:asset];
AVAssetResourceLoader *loader = asset.resourceLoader;
[loader setDelegate:self queue:dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)];
// AVPlayer
AVPlayer *avPlayer = [AVPlayer playerWithPlayerItem:playerItem];
You will need to handle the resourceLoader:shouldWaitForLoadingOfRequestedResource:delegate method which will be called when there is an authentication need and you can use NSURLConnection to request for the secured resource.
(BOOL)resourceLoader:(AVAssetResourceLoader *)resourceLoader shouldWaitForLoadingOfRequestedResource:(AVAssetResourceLoadingRequest *)loadingRequest
{
//Handle NSURLConnection to the SSL secured resource here
return YES;
}
Hope this helps!
P.S : The proxy approach using CocoaHTTPServer works well but using an AVAssetResourceLoader is a more elegant solution.
It seems that until Apple lets us control what NSURLConnections the AVPlayer uses the only answer seems to be to implement a HTTP loopback server.
To quote the apple representative that answered our support question:
Another option is to implement a loopback
HTTP server and point client objects at that. The clients can use
HTTP (because their requests never make it off the device), while the
loopback HTTP server itself can use HTTPS to connect to the origin
server. Again, this gives you access to the underlying
NSURLConnections, allowing you to do custom server trust evaluation.
Using this technique with UIWebView is going to be tricky unless you
completely control the content at the origin server. If the origin
server can return arbitrary content, you have to grovel through the
returned HTTP and rewrite all the links, which is not much fun. A
similar restriction applies to MPMoviePlayerController/AVPlayer, but
in this case it's much more common to control all of the content and
thus be able to avoid non-relative links.
EDIT:
I managed to implement a loopback server using custom implemenations of the
HTTPResponse and HTTPConnection classes found in CocoaHTTPServer
I can´t disclose the source, but I used NSURLConnection together with a mix of the AsyncHTTPResponse and DataHTTPResponse demonstration responses.
EDIT:
Remember to set myHttpServerObject.interface = #"loopback";
EDIT: WARNING!!! This approach does not seem to work with airplay since the airplay device will ask 127.1.1.1 for encryption keys. The correct approach seems to be defined here:
https://developer.apple.com/library/content/documentation/AudioVideo/Conceptual/AirPlayGuide/EncryptionandAuthentication/EncryptionandAuthentication.html#//apple_ref/doc/uid/TP40011045-CH5-SW1
"Specify the keys in the .m3u8 files using an application-defined URL scheme."
EDIT:
An apple TV and iOS update has resolved the issue mentioned in the edit above!
After more than a few hours of searching, I got in what looks like a dead end. In this case, all that I am trying to do, is to get all the iOS Devices of the network with Bonjour. I did so like this
self.serviceBrowser = [[NSNetServiceBrowser alloc] init];
[self.serviceBrowser setDelegate:self];
[self.serviceBrowser searchForServicesOfType:#"_apple-mobdev2._tcp." inDomain:#"local."];
This works fine, though what I get is the following:
local. _apple-mobdev2._tcp. [MAC ADDRESS HERE]
I tried to resolve the connection by using the sync port (62078), since service.port returns -1.
for (NSNetService *service in self.services) {
NSLog(#"%#", service);
NSNetService *newService = [[NSNetService alloc] initWithDomain:service.domain type:service.type name:service.name port:62078];
[newService setDelegate:self];
[newService resolveWithTimeout:30];
}
This in its own turn calls netServiceWillResolve: with no problem at all, but, it doesn't make it to netServiceDidResolveAddress:
But neither does this fail. netService:didNotResolve: isn't called either, I believe it is just waiting for a response to be resolved.
To support this claim, once it did make it to the method and actually [service hostName]; did return Yanniss-iPhone, but that happened at a completely random time that I had left the Mac App running for around half an hour. What could have invoked this to run? Or does anyone know of a different way to get the hostName of the remote device? The other answers do not answer my question, since I am looking for the hostName of the remote device, not of the Mac device.
Relative to that, I've found that when you kill and restart iTunes, along with iTunes Helper, the very log I mentioned below is sent again. Which is why I believe the correct log was an iTunes related event. Any help is very much appreciated!
iTunes search bonjour for wifi sync capability. As for the didNotResolve or resolve delay, bonjour services randomly cast itself anywhere between a few seconds to 30 minutes.
I am actually trying to connect to iOS devices too, but I could not get any response or any devices returned. :\