en0 seems to be the wifi interface for Apple iOS devices, but in my code, small multicast client when I specify the interface en0 I'm not receiving anything. Any clue of what could be wrong ? GDCasyncUdpSocket logs don't show any error =>
Binding socket to port(1234) interface((en0))
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil {
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
//log test
[DDLog addLogger:[DDTTYLogger sharedInstance]];
// Create multicast High Priotity queue
mcastQueue = dispatch_queue_create("mcastQueue", NULL);
dispatch_queue_t high = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0);
dispatch_set_target_queue(mcastQueue, high);
// Create UDP Socket
mcastSocket=[[GCDAsyncUdpSocket alloc] initWithDelegate:self delegateQueue:mcastQueue];
[mcastSocket setPreferIPv4];
return self;
}
- (void)viewDidLoad {
[super viewDidLoad];
NSError *socketError=nil;
if (![mcastSocket bindToPort:1234 interface:#"en0" error:&socketError]) {
NSLog(#"Failed binding socket to port: %#" ,socketError);
return;
}
if (![mcastSocket enableBroadcast:YES error:&socketError]) {
NSLog(#"Failed enabling broadcast: %#" ,socketError);
return;
}
if (![mcastSocket joinMulticastGroup:#"239.0.0.1" error:&socketError]) {
NSLog(#"Failed joining multicast group: %#" ,socketError);
return;
}
//start receiving multicast data
if (![mcastSocket beginReceiving:&socketError]) {
[mcastSocket close];
NSLog(#"Failed to start receiving: %#" ,socketError);
} else {
NSLog(#"Multicast start receiving");
}
}
Regards
After a year or so of intermittently trying to solve this and giving up, I finally discovered what I believe is the answer to this.
The answer is that you should not bind to an interface when receiving UDP packets. (See https://stackoverflow.com/a/10739443/179216)
Instead you should specify the interface when joining the multicast group:
// Do *not* specify the interface here
if (![mcastSocket bindToPort:1234 error:&socketError]) {
NSLog(#"Failed binding socket to port: %#" ,socketError);
return;
}
if (![mcastSocket enableBroadcast:YES error:&socketError]) {
NSLog(#"Failed enabling broadcast: %#" ,socketError);
return;
}
// This is where you specify the interface
if (![mcastSocket joinMulticastGroup:#"239.0.0.1" onInterface:#"en0" error:&socketError]) {
NSLog(#"Failed joining multicast group: %#" ,socketError);
return;
}
Related
I'm always getting TURN Connection failed. This is the code I'm using
- (void)viewDidLoad {
XMPPJID *jid = [XMPPJID jidWithString:chatWithUser resource:#"Smack"];
[TURNSocket setProxyCandidates:[NSArray arrayWithObjects:chatWithUser, nil]];
TURNSocket *turnSocket = [[TURNSocket alloc] initWithStream:[self xmppStream] toJID:jid];
[turnSockets addObject:turnSocket];
[turnSocket startWithDelegate:self delegateQueue:dispatch_get_main_queue()];
}
- (void)turnSocket:(TURNSocket *)sender didSucceed:(GCDAsyncSocket *)socket {
NSLog(#"TURN Connection succeeded!");
NSLog(#"You now have a socket that you can use to send/receive data to/from the other person.");
[turnSockets removeObject:sender];
}
- (void)turnSocketDidFail:(TURNSocket *)sender {
NSLog(#"TURN Connection failed!");
[turnSockets removeObject:sender];
}
In a "getMyFile" method of some XIB file.
I am creating a object of class "A"(subclass of NSOperation) and adding it to a "myFileQueue"(object of NSOperationQueue).
myFileQueue.MaxConcurrentOperationCount = 1;
Problem : didReadDataWithTag" delegate NEVER called in any case.
#import "GCDAsyncSocket.h"
//and all other required classes are imported correctly
//Class : A
#interface A : NSOperation
{
{
GCDAsyncSocket* socket;
dispatch_queue_t dQueue;
BOOL isWorkDone;
}
}
#implementation A {
-main(){
#autoreleasepool {
isWorkDone = NO;
dQueue = dispatch_queue_create(#"MyDQueue", NULL);
socket = [[GCDAsyncSocket alloc] initWithDelegate:self delegateQueue:dQueue];
//NOTE: Even after setting delegateQueue: dispatch_get_main_queue()
//didReadDataWithTag delegate never called
NSError *err = nil;
if (![socket connectToHost:#"192.168.1.142" onPort:12345 error:&err]) // Asynchronous!
{
// If there was an error, it's likely something like "already connected" or "no delegate set"
NSLog(#"I goofed: %#", err);
}
do{
[NSThread sleep:0.2];
}while(!isWorkDone)
}
}
- (void)socket:(GCDAsyncSocket *)sender didConnectToHost:(NSString *)host port:(UInt16)port
{
NSString* myString= #"testing";
NSData* data=[myString dataUsingEncoding: [NSString defaultCStringEncoding] ];
[socket writeData:data withTimeout:-1 tag:1];
}
- (void)socket:(GCDAsyncSocket *)sock didWriteDataWithTag:(long)tag
{
[socket readDataToData:[GCDAsyncSocket ZeroData] withTimeout:-1 tag:TAG_RESPONSE_HEADER];
}
- (void)socket:(GCDAsyncSocket *)sender didReadData:(NSData *)data withTag:(long)tag
{
NSLog(#"didReadDataWithTag called...."); //this method never called in any case
isWorkDone = YES;
}
}
This question already has answers here:
How can I check for an active Internet connection on iOS or macOS?
(46 answers)
Closed 9 years ago.
Which is the best way to check the Internet connection using the iOS SDK?
Best way is to use Reachability code. Check here for apple sample code. That has a lot of convenience methods to check internet availability, Wifi/WAN connectivity check etc..
For eg:-
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(networkChanged:) name:kReachabilityChangedNotification object:nil];
reachability = [Reachability reachabilityForInternetConnection];
[reachability startNotifier];
- (void)networkChanged:(NSNotification *)notification
{
NetworkStatus remoteHostStatus = [reachability currentReachabilityStatus];
if(remoteHostStatus == NotReachable) { NSLog(#"not reachable");}
else if (remoteHostStatus == ReachableViaWiFiNetwork) { NSLog(#"wifi"); }
else if (remoteHostStatus == ReachableViaCarrierDataNetwork) { NSLog(#"carrier"); }
}
Reachability has a lot more to it than it needs, plus it hasn't been updated for ARC yet.
Here's my solution in pure C. Much of the code was taken directly from Reachability, but distilled down to only what is necessary. I only wanted it to return whether there was or wasn't an internet connection, but you can read from the comments whether it's returning YES based on having found a Wifi or a Cellular network.
One last note before proceeding to share the code: You need to go into your build target, select the build phases tab, and add "SystemConfiguration.framework" to the "Link Binary With Libraries" list.
#import <CoreFoundation/CoreFoundation.h>
#import <SystemConfiguration/SystemConfiguration.h>
#import <netdb.h>
BOOL networkReachable()
{
struct sockaddr_in zeroAddress;
bzero(&zeroAddress, sizeof(zeroAddress));
zeroAddress.sin_len = sizeof(zeroAddress);
zeroAddress.sin_family = AF_INET;
SCNetworkReachabilityRef reachabilityRef = SCNetworkReachabilityCreateWithAddress(kCFAllocatorDefault, (const struct sockaddr *) &zeroAddress);
SCNetworkReachabilityFlags flags;
if (SCNetworkReachabilityGetFlags(reachabilityRef, &flags)) {
if ((flags & kSCNetworkReachabilityFlagsReachable) == 0) {
// if target host is not reachable
return NO;
}
if ((flags & kSCNetworkReachabilityFlagsConnectionRequired) == 0) {
// if target host is reachable and no connection is required
// then we'll assume (for now) that your on Wi-Fi
return YES; // This is a wifi connection.
}
if ((((flags & kSCNetworkReachabilityFlagsConnectionOnDemand ) != 0)
||(flags & kSCNetworkReachabilityFlagsConnectionOnTraffic) != 0)) {
// ... and the connection is on-demand (or on-traffic) if the
// calling application is using the CFSocketStream or higher APIs
if ((flags & kSCNetworkReachabilityFlagsInterventionRequired) == 0) {
// ... and no [user] intervention is needed
return YES; // This is a wifi connection.
}
}
if ((flags & kSCNetworkReachabilityFlagsIsWWAN) == kSCNetworkReachabilityFlagsIsWWAN) {
// ... but WWAN connections are OK if the calling application
// is using the CFNetwork (CFSocketStream?) APIs.
return YES; // This is a cellular connection.
}
}
return NO;
}
Try this code:
- (BOOL)connectedToInternet
{
NSURL *url=[NSURL URLWithString:#"http://www.google.com"];
NSMutableURLRequest *request=[NSMutableURLRequest requestWithURL:url];
[request setHTTPMethod:#"HEAD"];
NSHTTPURLResponse *response;
[NSURLConnection sendSynchronousRequest:request returningResponse:&response error: NULL];
return ([response statusCode]==200)?YES:NO;
}
I have successfully made a connection from an iPhone to a Server (which is a Windows machine) using TCP sockets. Currently, I'm using a button to execute the following code:
while(1)
{
Socket *socket;
int port = 11005;
NSString *host = #"9.5.3.63";
socket = [Socket socket];
#try
{
NSMutableData *data;
[socket connectToHostName:host port:port];
[socket readData:data];
// [socket writeString:#"Hello World!"];
//** Connection was successful **//
[socket retain]; // Must retain if want to use out of this action block.
}
#catch (NSException* exception)
{
NSString *errMsg = [NSString stringWithFormat:#"%#",[exception reason]];
NSLog(errMsg);
socket = nil;
}
}
That was the easy part... I'm trying to establish the sockets connection as soon as the app loads. I tried putting this code in my viewDidLoad, but the loop is infinite and the view never loads. I have several views in my project, and I'd like to open the connection keep the connection open at all times, across all views.
Objective:
Open TCP Sockets connection when app first loads
Maintain connection infinitely, no matter what view I am in (Multiple Views in Project)
I'm still rather new to iOS development, so I appreciate as much clarity as possible. It should be noted that I am using the SmallSockets library to open my Sockets connection. Thanks for the help!
* EDIT *
Based off the answer below, this is what I've got going so far:
SocketConnection.h
#import <Foundation/Foundation.h>
#interface SocketConnection : NSObject
{
}
+ (SocketConnection *)getInstance;
#end
SocketConnection.m
static SocketConnection *sharedInstance = nil;
#implementation SocketConnection
- (id)init
{
self = [super init];
if (self)
{
while(1)
{
Socket *socket;
int port = 11005;
NSString *host = #"9.5.3.63";
socket = [Socket socket];
#try
{
NSMutableData *data;
[socket connectToHostName:host port:port];
[socket readData:data];
// [socket writeString:#"Hello World!"];
//** Connection was successful **//
[socket retain]; // Must retain if want to use out of this action block.
}
#catch (NSException* exception)
{
NSString *errMsg = [NSString stringWithFormat:#"%#",[exception reason]];
NSLog(errMsg);
socket = nil;
}
}
}
return self;
}
+ (SocketConnection *)getInstance
{
#synchronized(self)
{
if (sharedInstance == nil)
{
sharedInstance = [[SocketConnection alloc] init];
}
}
return sharedInstance;
}
#end
I still have not figured out how the singleton class gets invoked. I fired up my app with the code from above and it's not connecting to the server. Any ideas?
Thanks!
You should create a singleton classes to keep your connection like below code:
h file:
#import <Foundation/Foundation.h>
#interface SocketConnection : NSObject
{
}
+ (SocketConnection *)getInstance;
#end;
m file:
#import "SocketConnection.h"
static SocketConnection *sharedInstance = nil;
#implementation SocketConnection
- (id)init
{
self = [super init];
if (self) {
}
return self;
}
+ (SocketConnection *)getInstance
{
#synchronized(self) {
if (sharedInstance == nil) {
sharedInstance = [[SocketConnection alloc] init];
}
}
return sharedInstance;
}
#end;
I have implemented an FTP client on the iPhone, but when the connection is interrupted for a moment while download from the FTP server is in progress, the application informs me there is no connection and stops the client.
Here comes the problem: next time i try to start the download process again, the event stream:handleEvent: is not fired and the streamStatus of the networkStream stays on NSStreamStatusOpening.
If I manually stop the download process (using the same method which I fire when connection is interrupted), I can then re-download again. I have to relaunch the whole application for the downloading to work again.
Here are the key parts of the code:
- (void)downloadFile:(NSDictionary *)file {
NSURL *url;
CFReadStreamRef ftpStream;
url = [NSURL URLWithString:[#"PATH TO FTP FILE" stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]];
[self.fileStream open];
ftpStream = CFReadStreamCreateWithFTPURL(NULL, (__bridge CFURLRef) url);
self.networkStream = (__bridge NSInputStream *) ftpStream;
self.networkStream.delegate = self;
[self.networkStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
[self.networkStream open];
CFRelease(ftpStream);
}
and the method that is fired when something happens with the stream
- (void)stream:(NSStream *)aStream handleEvent:(NSStreamEvent)eventCode {
switch (eventCode) {
case NSStreamEventOpenCompleted: {
[self updateStatus:#"Opened connection"];
} break;
case NSStreamEventHasBytesAvailable: {
NSInteger bytesRead;
uint8_t buffer[32768];
bytesRead = [self.networkStream read:buffer maxLength:sizeof(buffer)];
if (bytesRead == -1) {
[self stopReceivingWithStatus:#"Network read error"];
} else if (bytesRead == 0) {
[self stopReceivingWithStatus:nil];
} else {
[self processStreamDataWithBuffer:buffer andReadBytes:bytesRead];
}
} break;
case NSStreamEventHasSpaceAvailable: {
assert(NO);
} break;
case NSStreamEventErrorOccurred: {
[self stopReceivingWithStatus:#"Stream open error"];
} break;
case NSStreamEventEndEncountered: {
} break;
default: {
assert(NO);
} break;
}
The problem as I said is that after connection interrupted, the events are not fired anymore.
Note: I am using iOS 5 with ARC.
Perhaps you should set the flag to force the stream to close the underlying native socket which by default is not done;
[self.networkStream setProperty:(id)kCFBooleanTrue forKey:(NSString *)kCFStreamPropertyShouldCloseNativeSocket];