I need to check for reachability for a socket connection in my application. I have gone through apple's sample code and few examples I could find from SO and other websites but I'm out of luck as I do not get any response. Here's a sample code I'm trying.
My requirement is simply to verify connectivity to myhost:port which is a socket connection. myhost is an address from dyndns.org
struct sockaddr_in server_address;
server_address.sin_len = sizeof(server_address);
server_address.sin_family = AF_INET;
server_address.sin_port = htons(1234);
server_address.sin_addr.s_addr = inet_addr("host-from-dyndns.org");
Reachability *reachability = [Reachability reachabilityWithAddress:&server_address];
__weak Reachability *weakReachability = reachability;
reachability.reachableBlock = ^(Reachability*reach)
{
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(#"REACHABLE!");
});
[weakReachability stopNotifier];
};
reachability.unreachableBlock = ^(Reachability*reach)
{
NSLog(#"UNREACHABLE!");
[weakReachability stopNotifier];
};
[reachability startNotifier];
After few failed attempts of meddling with with reachability classes, I gave up and used a temporary asynchronous socket connection created on demand to verify the connection. It works as expected and is built on top of gcd. Also I found the AsyncSocket by Robbie Hanson (https://github.com/robbiehanson/CocoaAsyncSocket) to be quite helpful while implementing for my requirement.
Related
Basically I would like to be able to check whether a particular host on my local network is "up".
The following line of code hangs when the host is unreachable, so I'd like to perform a check first before running it.
[_outputStream write:[data bytes] maxLength:[data length]];
A similar query I think is answered in the following link, but I think I need to use CFHostCreateWithAddress rather than CFHostCreateWithName
Alternatives to NSHost in iPhone app
Here is my attempt at what I'm trying to do...
Boolean result;
struct sockaddr_in address;
address.sin_family = AF_INET;
address.sin_port = htons(80);
inet_pton(AF_INET, "192.168.1.31", &address.sin_addr);
CFDataRef sockData = CFDataCreate(NULL, &address, sizeof(address));
CFHostRef host = CFHostCreateWithAddress(NULL, sockData);
result = CFHostStartInfoResolution(host, kCFHostAddresses, NULL);
if (result == TRUE) {
NSLog(#"Resolved");
} else {
NSLog(#"Not resolved");
}
Even when the host is up I get Not resolved.
Below is my attempt to use the Reachability class. My code is telling me the below address is reachable despite there being no host at the specified address.
struct sockaddr_in address;
address.sin_family = AF_INET;
address.sin_port = htons(80);
inet_pton(AF_INET, "192.168.1.31", &address.sin_addr);
Reachability *reachability = [Reachability reachabilityWithAddress:&address];
NetworkStatus reachabilitytoHost = [reachability currentReachabilityStatus];
if(reachabilitytoHost != NotReachable)
{
NSLog(#"Reachable");
}
else
{
NSLog(#"Not Reachable");
}
Add Reachability Class to your project.
#import "Reachability.h"
also add SystemConfiguration framework.
Reachability *reachability = [Reachability reachabilityWithHostName:#"www.example.com"];
NetworkStatus reachabilitytoHost = [reachability currentReachabilityStatus];
if(reachabilitytoHost != NotReachable)
{
//reachable
}
else
{
// not reachable
}
Check sample code here: https://developer.apple.com/library/ios/samplecode/Reachability/Introduction/Intro.html
For more info: https://developer.apple.com/library/ios/samplecode/Reachability/Listings/Reachability_Reachability_h.html
Take a look at the Reachability class by Tony Million: https://github.com/tonymillion/Reachability
From the Read Me:
// Allocate a reachability object
Reachability* reach = [Reachability reachabilityWithHostname:#"www.google.com"];
// Set the blocks
reach.reachableBlock = ^(Reachability*reach)
{
// keep in mind this is called on a background thread
// and if you are updating the UI it needs to happen
// on the main thread, like this:
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(#"REACHABLE!");
});
};
reach.unreachableBlock = ^(Reachability*reach)
{
NSLog(#"UNREACHABLE!");
};
// Start the notifier, which will cause the reachability object to retain itself!
[reach startNotifier];
Where you could obviously replace the www.google.com with whatever address you would like to test.
I am making an app to send UDP packets in order to switch on a LED bulb. I have been able to perform all the actions when I am connecting to the Ad-hoc created by the Wifi bridge.
Now, I want to configure the Wifi bridge so that it can connect to my main router. I have the AT command set to perform this procedure but somehow I am not able to receive the response form the Wifi bridge for the commands which I am sending to it.
The procedure is as follows:-
Step 1 : Send UDP message to the LAN broadcast IP address of "10.10.100.255" and port of 48899 => "Link_Wi-Fi"
All Wifi bridges on the LAN will respond with their details. Response is "10.10.100.254, ACCF232483E8"
Step 2 : (optional for changing settings on the wifi bridge): Then send "+ok" to the LimitlessLED Wifi Bridge. Send UDP message to the response IP address returned from step 1 "10.10.100.254" => "+ok"
Step 3 : (optional for changing settings on the wifi bridge): After that you may send AT commands (ending with \r\n) to the module.
The code for sending the UDP packets is as follows
-(void)configureWifi{
counter++;
NSString *host = #"10.10.100.255";
if ([host length] == 0)
{
[self logError:#"Address required"];
return;
}
int port = 48899; //[portField.text intValue];
if (port <= 0 || port > 65535)
{
[self logError:#"Valid port required"];
return;
}
NSString *msg = #"Link_Wi-Fi";
NSData *data = [msg dataUsingEncoding:NSUTF8StringEncoding];
NSLog(#"the message sent is %#", data);
[udpSocket sendData:data toHost:host port:port withTimeout:-1 tag:tag];
}
Now in order to setup the socket and to receive the data I am using these two delegate methods:
- (void)setupSocket
{
// Setup our socket.
// The socket will invoke our delegate methods using the usual delegate paradigm.
// However, it will invoke the delegate methods on a specified GCD delegate dispatch queue.
//
// Now we can configure the delegate dispatch queues however we want.
// We could simply use the main dispatc queue, so the delegate methods are invoked on the main thread.
// Or we could use a dedicated dispatch queue, which could be helpful if we were doing a lot of processing.
//
// The best approach for your application will depend upon convenience, requirements and performance.
//
// For this simple example, we're just going to use the main thread.
udpSocket = [[GCDAsyncUdpSocket alloc] initWithDelegate:self delegateQueue:dispatch_get_main_queue()];
NSError *error = nil;
if (![udpSocket bindToPort:0 error:&error])
{
[self logError:FORMAT(#"Error binding: %#", error)];
return;
}
if (![udpSocket beginReceiving:&error])
{
[self logError:FORMAT(#"Error receiving: %#", error)];
return;
}
[self logInfo:#"Ready"];
}
and to Receive data this is the method which is note getting called after sending the UDP packets. This is the delegate method of the GCDAsyncUdpSocket class which I have used in my project in order to send and receive the UDP packets.
- (void)udpSocket:(GCDAsyncUdpSocket *)sock didReceiveData:(NSData *)data
fromAddress:(NSData *)address
withFilterContext:(id)filterContext
{
NSString *msg = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
if (msg)
{
[self logMessage:FORMAT(#"RECV: %#", msg)];
}
else
{
NSString *host = nil;
uint16_t port = 0;
[GCDAsyncUdpSocket getHost:&host port:&port fromAddress:address];
[self logInfo:FORMAT(#"RECV: Unknown message from: %#:%hu", host, port)];
}
}
Once I am able to receive the response I will be able to send the next AT commands in order to configure the Bridge.
Thanks. Any help will be appreciated.
Here are the troubleshooting steps I recommend that you use :
1- I'm assuming you are using ARC so make sure that your udpSocket variable has a strong reference throughout the asynchronous communication. If it is being freed, then that could explain the absence of a callback.
2- Make sure the communication is really happening the way you think it is. Use a software such as Wireshark to capture the packets being exchanged on the network. This should allow you to confirm that your packets do get sent upon calling sendData: and it will also allow you to confirm whether or not you are getting a reply back.
3- Make sure you are using the GCDAsyncUdpSocket properly. Considering you want to broadcast a message, you shouldn't be calling bindToPort:error: in your setupSocket method. Instead you should be calling enableBroadcast:error:. Considering you also want to receive packets after broadcasting, you should use the connectToHost:onPort:error: method to change the state of the socket to allow for bidirectional communication. After that is done, you can replace your usage of sendData:toHost:port:withTimeout:tag: by sendData:withTimeout:tag:. Finally, you can call beginReceiving: so that the delegate gets called for any incoming packets.
4- If this still doesn't get you through it, I recommend that you read throughly the documentation of the GCDAsyncUdpSocket which is very well documented.
You can trouble shoot the problem using Wireshark or any network capture tool.
We use to work in similar kind of project where we used Wireshark extensively.
If packet has reached device(Z-Wave ) it will send out some sort of Ack.
this will help to make sure packets are getting out.
Psuedocode of my current solution:
if (disconnected):
while (disconnected):
check for connection
if (connected):
fetch results
Is there a more idiomatic way to tell when the device goes from being disconnected to establishing an internet connection?
Take a look at Apple's Reachability sample code
You don't have to investigate detailed implementation of Reachability.m. You can just use it as a library.
Absolutely! This is what SCNetworkReachability is for!
Unfortunately, people tend to use it incorrectly. What you are describing would be a correct use case for it.
Attempt a connection normally.
If it fails with NSURLErrorNotConnectedToInternet:
Use SCNetworkReachability to monitor the device network configuration for changes
When the network configuration moves to a state that indicates packets can again leave the device, your application will be notified and you can make connections again.
What you should NOT do is try to use SCNetworkReachability to see if the device is connected before connecting. This is not recommended for many reasons. The SCNetworkReachability API can tell you when it is, or is not, possible for packets to leave the device. It can't tell you if the thing you're connecting to is down, DNS isn't working, etc.
The Apple sample projects MVCNetworking and Reachability demonstrate use of the SCNetworkReachability API.
In practice, this looks like....
In your connection error handling:
if ([[error domain] isEqualToString:NSURLErrorDomain]){
NSURL *failingURL = [[error userInfo] valueForKey:NSURLErrorFailingURLErrorKey];
switch ([error code]){
case NSURLErrorNotConnectedToInternet:
[self beginObservingReachabilityStatusForHost:[failingURL host]];
break;
default:
break;
}
}
The beginObservingReachabilityStatusForHost: and endObsvervingReachabilityStatusForHost: methods:
- (void) beginObservingReachabilityStatusForHost:(NSString *)host {
SCNetworkReachabilityRef reachabilityRef = NULL;
void (^callbackBlock)(SCNetworkReachabilityFlags) = ^(SCNetworkReachabilityFlags flags) {
BOOL reachable = (flags & kSCNetworkReachabilityFlagsReachable) != 0;
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
[self host:host didBecomeReachable:reachable];
}];
};
SCNetworkReachabilityContext context = {
.version = 0,
.info = (void *)CFBridgingRetain(callbackBlock),
.release = CFRelease
};
if ([host length] > 0){
reachabilityRef = SCNetworkReachabilityCreateWithName(kCFAllocatorDefault, [host UTF8String]);
if (SCNetworkReachabilitySetCallback(reachabilityRef, ReachabilityCallback, &context)){
if (!SCNetworkReachabilitySetDispatchQueue(reachabilityRef, [self scNetworkQueue]) ){
// Remove our callback if we can't use the queue
SCNetworkReachabilitySetCallback(reachabilityRef, NULL, NULL);
}
[self setCurrentReachability:reachabilityRef];
}
}
}
- (void) endObsvervingReachabilityStatusForHost:(NSString *)host {
// Un-set the dispatch queue
if (!SCNetworkReachabilitySetDispatchQueue([self currentReachability], NULL) ){
}
SCNetworkReachabilitySetCallback([self currentReachability], NULL, NULL);
}
The C callback that wraps our block:
static void ReachabilityCallback(SCNetworkReachabilityRef target, SCNetworkConnectionFlags flags, void* info) {
void (^callbackBlock)(SCNetworkReachabilityFlags) = (__bridge id)info;
callbackBlock(flags);
}
And the method that the block calls to do something when reachability changes:
- (void) host:(NSString *)host didBecomeReachable:(BOOL)reachable {
// Update your UI to indicate reachability status here
if (reachable){
[self endObsvervingReachabilityStatusForHost:nil];
}
}
Obviously to use the above you need a serial dispatch queue and a way to hold on to the SCNetworkReachabilityRef for later cancellation when you're done.
I started using AFNetworking's reachability manager instead of Apple's Reachability class but it doesn't work. Its reachability status is AFNetworkReachabilityStatusUnknown despite the internet connection is turned on on the device. This is my code:
reachabilityManager = [AFNetworkReachabilityManager sharedManager];
[reachabilityManager startMonitoring];
[reachabilityManager setReachabilityStatusChangeBlock:^(AFNetworkReachabilityStatus status){
NSLog(#"Current status %#", reachabilityManager.localizedNetworkReachabilityStatusString);
}];
I put NSLog inside the block but i don't mean to say that i see results there. Actually the block is never called (while my device has normal internet connection). I just check the status before performing my first network request and i see it is -1 i.e. unknown. What's wrong?
This is how i check reachability status:
- (BOOL)connected
{
#if TARGET_IPHONE_SIMULATOR
return YES;
#else
NSLog(#"Status %d", reachabilityManager.networkReachabilityStatus);
return reachabilityManager.reachable;
#endif
}
i m using reachability classes to check wifi connectivity in my code. But sometimes problem arise that wifi is ON but there is no or low internet connectivity, here my code runs in loop waiting for any response from called webservice and hangsup and crashes sometimes.
Below Code is executed when i hit OK on an AlertView which pulls some data from webservice
Here is my code :
Reachability *ReachObj = [Reachability reachabilityForInternetConnection];
[ReachObj startNotifier];
NetworkStatus remoteHostStatus = [ReachObj currentReachabilityStatus];
if (remoteHostStatus==ReachableViaWiFi)
{
SecondView *ObjSecView=[[SecondView alloc]init];
[self presentModalViewController:ObjSecView animated:YES];
}
else
if (remoteHostStatus==NotReachable)
{
FirstView *objFrstView=[[FeedBackPopOverViewController alloc]init];
[self presentModalViewController:objFrstView animated:YES];
}
Guys i m new to Objective C.
Plz help me out, thanks in advance. And sorry for my grammatical mistakes.
try this..
SCNetworkReachabilityFlags flags;
SCNetworkReachabilityRef reachability=SCNetworkReachabilityCreateWithName(NULL, [#"your web sevice url" UTF8String]);
SCNetworkReachabilityGetFlags(reachability, &flags);
BOOL reachable=!(flags & kSCNetworkReachabilityFlagsConnectionRequired);
CFRelease(reachability);
NSURLRequest *request=[NSURLRequest requestWithURL:[NSURL URLWithString:urlString]];
if([NSURLConnection canHandleRequest:request] && reachable)
{
conn=[NSURLConnection connectionWithRequest:request delegate:self];
if(conn)
{
////
}
else
{
[_delegate performSelector:#selector(httpDataDidFailLoadingWithReason:)
withObject:#"No Internet Connection" afterDelay:0.1];
}
}
-(void) httpDataDidFailLoadingWithReason:(NSString*)reason
{
UIAlertView *alertView=[[UIAlertView alloc]initWithTitle:#"abc"
message:reason
delegate:self cancelButtonTitle:#"OK"
otherButtonTitles:nil];
[alertView show];
[alertView release];
}
-(void)loginButtonTouched
{
bool success = false;
const char *host_name = [#"www.google.com"
cStringUsingEncoding:NSASCIIStringEncoding];
SCNetworkReachabilityRef reachability = SCNetworkReachabilityCreateWithName
(NULL, host_name);
SCNetworkReachabilityFlags flags;
success = SCNetworkReachabilityGetFlags(reachability, &flags);
bool isAvailable = success && (flags & kSCNetworkFlagsReachable) &&
!(flags & kSCNetworkFlagsConnectionRequired);
if (isAvailable)
{
NSLog(#"Host is reachable: %d", flags);
// Perform Action if Wifi is reachable and Internet Connectivity is present
}
else
{
NSLog(#"Host is unreachable");
// Perform Action if Wifi is reachable and Internet Connectivity is not present
}
}
When loginButtonTouched method is called we check that "www.google.com" is reachable or not.
SCNetworkReachabilityFlags returns flags which helps us to understand the Status of internet connectivity.
If isAvailable variable returns "true" then Host is Reachable means Wifi is reachable and Internet Connectivity is present.
And thanks to all for providing with your quick answers to our questions.
Sorry all for my grammatical mistakes.