I've looked at Reachability sample from apple and another sample from "Iphone developer's cook book"
Here are steps to test ip/port reachability
construct sockaddr_in variable with given ip/port.
call SCNetworkReachabilityCreateWithAddress with the address
call SCNetworkReachabilityGetFlags and see if kSCNetworkFlagsReachable is set.
I find that whatever valid(valid-range) ip/port I put to test, my code says it's reachable even though they are not actually reachable when i ping or telnet test.
(I have seen other SO posts where reachability test to specific ip always succeed)
Reachability reachabilityWithAddress does not work
is SCNetworkReachability.. call only checks the validity of argument(ip address or hostname)? ie, do they actually send a packet(or do connect) to the given address to test reachability?
I can implement the async connect with timeout myself, but it's subtle matter how long the timeout should be not to be rejected by apple reviewers.(I am worried if it takes too long, apple reviewers might think my app is not handling network reachability at all)
Thank you
To answer your question based on the comments above, according to the SCNetworkReachability Reference
A remote host is considered reachable
when a data packet, sent by an
application into the network stack,
can leave the local device.
Reachability does not guarantee that
the data packet will actually be
received by the host.
This will not be able to check the status of your server but just make sure it can get a data packet out.
Apple wants for your app to elegantly fail if there are any network related issue, checking to see if internet is available using Reachability is the first step. If Reachability fails you can assume that you wont be able a server and alert the user immediately. The next steps are to properly handle errors returned by your server such as a 400 bad request, 404 file not found, setting a reasonable timeout, and not blocking the UI during long downloads while showing some sort of status indicator.
Related
What I want to do is call some code when the internet connection is established or restored.
I know there are solutions out there that let me test whether an internet connection is active or not, but I am wondering if there is a way for a function to be triggered by the internet connection becoming active.
If not, what is the best workaround? Checking for a connection every x seconds? Thanks.
The SCNetworkReachability APIs can be configured to generate notifications when the reachability status of an IP address or hostname changes. Create a SCNetworkReachabilityRef, then set a callback, and it will be called when a network change is detected.
I'm trying to generate ARP (Address Resolution Protocol) request packets on the iPhone and listen for the associated responses that come back.
Google searches have led me into a dead-end. In order to send logical-layer packets, I'd need something along the lines of a raw socket, but need super-user permissions to create them. I'm trying to avoid jailbreaking my phone.
There's lots of c code out there that can do this, but I can't find anything that can translate to iOS due to the permissions.
I was ready to throw in the towel when I decided to Wireshark a couple network discovery apps I have. Namely "Fing" and "Pinggy" (hats off to Fing and Pinggy btw... awesome apps!)
https://itunes.apple.com/us/app/pinggy/id562201096?mt=8
https://itunes.apple.com/us/app/fing-network-scanner/id430921107?mt=8
Running Wireshark alongside these iPhone apps shows that they do an ARP scan from XXX.XXX.X.0 all the way to XXX.XXX.X.255. I do not see any ICMP packets go out simultaneously with the "ARPs". This leads me to believe that sending and receiving ARP packets are indeed possible on iOS.
I've thought about a ping sweep, assuming that it will generate ARP requests on its own. However, I will still need a raw socket to listen to the responses, correct?
Questions: What's available for sending/receiving packets at the logical layer? Specifically for sending receiving ARP packets? Am I missing anything fundamental?
Thanks in advance!
ARP requests do go out when I attempted to ping the problematic devices. This was seen with a Wireshark session running alongside the ping scanner. I found that I could not reproduce the "missing devices" I was seeing earlier that led me to ask my original question.
So, to answer my own question: ARP requests are sent per IP address when doing a simple ping scan on my subnet. I would see the ARP request go out (using Wireshark) as well as the ping request. If you need to generate an ARP request, simply send out a ping.
Even if the "problematic" device won't respond to ping requests, the ARP table will be notified of its existence.
You can't do what you want to do, and get the app in the AppStore,
since what you are trying to do isn't in the public API.
So one thing you could do, for testing purposes on your own network, or enterprise distributed apps is looking in the private/undocumented APIs.
One such list is maintained at https://github.com/nst/iOS-Runtime-Headers, but I can't vouch for its accuracy.
Good luck!
In my app if user do not have internet connection I need to show "NO internet connection" message.
To use Reachability like that:
Reachability *netReach = [Reachability reachabilityWithHostName:#"host.name"];
is bad idea, because internet could be available but host is not, right ?
So how to check internet connection without host ? Thanks...
Reachability *internetReachable = [Reachability reachabilityForInternetConnection];
[internetReachable startNotifier];
if(internetReachable.isReachable)
NSLog(#"True");
You are absolutely right. Reachability only checks whether a certain host is reachable or not. The problem is, that without sending a request to a host there is no way (that I can think of) how to determine whether you are connected to the internet or not.
So what you can do (and what Apple is doing in its Reachability example): Choose a host where you can be 99.99% sure that the host is available. Like for example google.com or apple.com
I think the fact that apple is relying on a certain host to be available to check internet connection (and the fact that they haven't come up with something else, without dependency on a certain host) is a pretty good sign that checking internet connection in this manner is not such a bad idea.
EDIT
Of course the best way (as described in the answer to the question that Petesh is pointing to) would be to simply make the request you want to make and then handle errors accordingly if they occur. In other words: 1. Make your request 2. If it fails use Reachability to see if a missing internet connection might be the problem
Our web service has a "ping" function which is great for testing if the web service is available or not (I don't really care if the internet connection is available, right?) and I know how to test this condition, but how do I go about implementing this in my application? As in where do I test?
If there is no connection, the app doesn't crash of course, it just returns an empty table view or set of views. Should I put the ping before each request and generate an exception or error message when the ping fails? The web service request + response itself can take longer than the transmission of the actual data (latency I guess it would be) so I worry that implementing a Ping before each request might almost double the time it takes to perform each request. Or, should I be continually ping-ing and making sure there is a connection? What is the best practice?
Or should I even worry about it at all?
I was able to use the Reachability library as seen in the answer to How to check for an active Internet connection on iOS or OSX? . You can extend the library to include not only hostReachable but wsReachable with a little bit of work that is probably obvious to all.
iOS can provide you with callbacks once the network connectivity changes (e.g. from airplane mode to cell only to wifi and back). So you get notified whenever the network state changes and you don't need to keep pinging the server and can display proper online/offline messages.
Have a look at the SCNetworkReachabilitySetCallback method in the NetworkReachability.h header.
I am trying to access 61616 in my iPhone app and the SCReachability code indicates that I have a valid network, but I time out trying to write to 61616. Does anyone know:
A. If the iPhone EDGE/3G network has a limited port range or isn't allowed to connect to this port?
B. How I can check explicitly for access to this port. Here is my "is the network reachable" code (borrowed from Apple's examples), which checks for "foo.bar.com" but doesn't show how to check for "foo.bar.com:61616".
Any help is appreciated!
- (BOOL)isDataSourceAvailable{
static BOOL checkNetwork = YES;
BOOL _isDataSourceAvailable = NO;
if (checkNetwork) { // Since checking the reachability of a host can be expensive, cache the result and perform the reachability check once.
checkNetwork = NO;
Boolean success;
const char *host_name = "foo.bar.com";
SCNetworkReachabilityRef reachability = SCNetworkReachabilityCreateWithName(NULL, host_name);
SCNetworkReachabilityFlags flags;
success = SCNetworkReachabilityGetFlags(reachability, &flags);
_isDataSourceAvailable = success && (flags & kSCNetworkFlagsReachable) && !(flags & kSCNetworkFlagsConnectionRequired);
CFRelease(reachability);
}
return _isDataSourceAvailable;
}
The SCNetworkReachability API only checks whether the address can be routed to. Quoting the documentation:
"Reachability" reflects whether a data
packet, sent by an application into
the network stack, can leave the local
computer. Note that reachability does
not guarantee that the data packet
will actually be received by the host.
This has nothing to do with NAT or port restrictions. Routing is based on addresses - not on ports. So all you can find out (via SCNetworkReachability API) is whether you could (in theory) reach the machine's interface. It does not try to establish a connection on a certain port.
So in order to find out whether that service is reachable you will have to try and test yourself. Preferably when the SCNetworkReachability API tells you that a connection can be established. Just try to connect to the server on port 61616 and see whether you get a connection or a timeout. Proper timeout values and handling is crucial here.
This is something that varies a lot from not just carrier to carrier but from contract to contract. For example, here in the UK O2 restrict internet access only to web addresses (presumably ports 80 and 443 but I didn't check) for pay-as-you-go customers but allow more-or-less free access for people on a contract. (I would hope that it's a little more open for iPhone PAYG customers.)
I think the practical upshot of this is that you can't assume access to high ports everywhere in the world.
I'm not sure that there's a simple way to check that a particular port is open, probably your best option is to use a "reasonable" timeout. Of course, there are a number of reasons you might get the timeout so your error would have to be suitable vague.
If your iPhone is connected over EDGE/Mobile, it might be behind a NAT gateway thus you being unable to contact it. At least that's the case with T-Mobile Germany. The iPhone gets an RFC IP address by T-Mobile (172.16.x.x).
I would test it with the iPhone connected to your local WiFi net and -- if possible -- generally design the app in a way that the iPhone contacts a server and then the server can communicate with the phone.
Are you trying to contact your iPhone from an external machine?
That likely does not work. Usually the GPRS/EDGE/UMTS infrastructure blocks incoming connections. Otherwise a mobile subscriber could be billed for unwanted traffic he did not request. (Imagine someone pinging your iPhone once per second for the next six weeks. You would pay for that...)
Solution is to establish a connection from the mobile device to the remote machine and then let the remote machine use that for traffic.