Weird BSD Sockets Issue

Discussion in 'Mac Programming' started by Matt342, Jun 12, 2008.

  1. macrumors regular

    Joined:
    Jul 20, 2007
    #1
    Hi!

    I'm writing a simple iPhone program that sends data to a BSD Sockets server. The program works fine with WiFi but EDGE is what I'm having problems with.

    If I start the client and no networking program has been opened prior to running the client, it won't connect to the server.

    While if I start Weather.app for example, and then my client program, everything works fine!

    Why does my app only work over EDGE when a networked program has been started before?

    ~Matt
     
  2. macrumors 6502a

    yeroen

    Joined:
    Mar 8, 2007
    Location:
    Cambridge, MA
    #2
    can you give us any error messages or source code?
     
  3. thread starter macrumors regular

    Joined:
    Jul 20, 2007
    #3
    the variable errno contains error code 1 which states - The user tried to connect to a broadcast addresswithout having the socket broadcast flag enabled orthe connection request failed because of a localfirewall rule.

    Any ideas? Why does my code work on EDGE when I start a networked program like Stocks or Weather prior to starting my client?

    ~Matt


     
  4. macrumors 603

    Cromulent

    Joined:
    Oct 2, 2006
    Location:
    The Land of Hope and Glory
    #4
    Because Weather or Stocks set the appropriate flag for you I would imagine.

    The error message tells you what the problem is and the solution.
     
  5. thread starter macrumors regular

    Joined:
    Jul 20, 2007
    #5
    How do I set the flags? Which method?

    Thanks!

     
  6. macrumors 603

    Cromulent

    Joined:
    Oct 2, 2006
    Location:
    The Land of Hope and Glory
    #6
    I think the flag is SO_BROADCAST and you add it to the setsockopt() function. Seems to require a UDP connection though (SOCK_DGRAM) which is a bit odd. Try it and see. If it does not work I'll have another look for you.

    Edit : Fixed a mistake above.
     
  7. thread starter macrumors regular

    Joined:
    Jul 20, 2007
    #7
    Thanks. I just tried it. It still won't work.

    Any other ideas?

    ~Matt

     
  8. macrumors 603

    Cromulent

    Joined:
    Oct 2, 2006
    Location:
    The Land of Hope and Glory
    #8
    Post the code you have.
     
  9. thread starter macrumors regular

    Joined:
    Jul 20, 2007
    #9
    Socket.m (custom Class I made for wrapping BSD sockets)
    Code:
    - (id)initWithUnixSocket:(int)socket{
    	self = [super init];
    	if (self){
    		sock = socket;
    	}
    	return self;
    }
    
    - (int)createWithDomain:(int)domain Type:(int)type Protocol:(int)protocol{
    	sock = socket(domain, type, protocol);
    	if (sock == -1){
    		return ERROR;
    	}
    	return SUCCESS;
    }
    
    - (int)connectToAddress:(const char *)address onPort:(unsigned int)port{
    	struct sockaddr_in data;
    	bzero(&data, sizeof(data));
    	data.sin_family = AF_INET;
    	data.sin_port = htons(port);
    	data.sin_addr.s_addr = inet_addr(address);
    	
    	int result = connect(sock, (const struct sockaddr *)&data, sizeof(data));
    	if (result == 0){
    		return SUCCESS;
    	}
    	printf("%i", errno);
    	return ERROR;
    }
    
    - (int)bindOnPort:(unsigned int)port{
    	struct sockaddr_in data;
    	bzero(&data, sizeof(data));
    	data.sin_family = AF_INET;
    	data.sin_port = htons(port);
    	data.sin_addr.s_addr = INADDR_ANY;
    	
    	int result = bind(sock, (const struct sockaddr *)&data, sizeof(data));
    	if (result == 0){
    		return SUCCESS;
    	}
    	return ERROR;
    }
    
    - (int)listenWithBackLog:(int)backLog{
    	int result = listen(sock, backLog);
    	if (result == 0){
    		listeningThread = [[NSThread alloc] initWithTarget:self selector:@selector(listeningThread:) object:nil];
    		[listeningThread start];
    		
    		return SUCCESS;
    	}
    	return ERROR;
    }
    
    - (void)listeningThread:(id)object{
    	NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
    	
    	int incomingClient = accept(sock, NULL, NULL);
    	if (incomingClient != -1){
    		Socket *incomingSocket = [[Socket alloc] initWithUnixSocket:incomingClient];
    		incomingSocket.delegate = self.delegate;
    		if ([delegate respondsToSelector:@selector(connectionAccepted:)]){
    			[delegate connectionAccepted:incomingSocket];
    		}
    	}
    	
    	[pool release];
    }
    
    - (int)sendData:(const char *)data{
    	int length = strlen(data);
    	
    	int result = send(sock, data, length, 0);
    	if (result > 0){
    		return result;
    	}
    	return ERROR;
    }
    
    - (void)waitForData{
    	dataThread = [[NSThread alloc] initWithTarget:self selector:@selector(listenForData:) object:nil];
    	[dataThread start];
    }
    
    - (void)listenForData:(id)object{
    	NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
    	
    	BOOL waitingForData = YES;
    	while (waitingForData){
    		const char *buffer[1024];
    		int length = sizeof(buffer);
    		int result = recv(sock, &buffer, length, 0);
    		
    		if (result > 0){
    			if ([delegate respondsToSelector:@selector(dataReceived:)]){
    				[delegate dataReceived:buffer];
    			}
    		}
    	}
    	
    	[pool release];
    }
    
    - (void)close{
    	close(sock);
    }
    
    Server Code (Uses Socket.m)

    Code:
    
    	if ([server createWithDomain:AF_INET Type:SOCK_STREAM Protocol:0] == SUCCESS){
    		[statusText setStringValue:STATUS_CREATED_SOCKET];
    		if ([server bindOnPort:PORT] == SUCCESS){
    			[statusText setStringValue:STATUS_BOUND];
    			if ([server listenWithBackLog:5] == SUCCESS){
    				[statusText setStringValue:STATUS_WAITING_FOR_CONNECTIONS];
    			}
    		}
    	}
    
    
    Client Code (Uses Socket.m):
    Code:
    	if ([client createWithDomain:AF_INET Type:SOCK_STREAM Protocol:0] == SUCCESS){
    		canConnect = YES;
    	}
    if (connected == NO){
    		if (canConnect){
    			if ([client connectToAddress:[ipField.text UTF8String] onPort:PORT] == SUCCESS){
    				connected = YES;
    			}	 
    		}
    	}
    
    
    [client connectToAddress:[ipField.text UTF8String] onPort:pORT] always fails on EDGE, unless a networked program is started before....
     
  10. thread starter macrumors regular

    Joined:
    Jul 20, 2007
    #10
    Any ideas?
     
  11. macrumors 603

    Cromulent

    Joined:
    Oct 2, 2006
    Location:
    The Land of Hope and Glory
    #11
    Well one mistake you have made is that you have declared your buffer in listenForData as a const which it is not. Const is used for strings that do not change. Your string is changed with the call to recv().

    I'd declare buffer using the easier to manage malloc method.

    Code:
    char *buffer = (char *) malloc (1024);
    
    free(buffer); // don't forget to put this at the end when you are finished with the buffer
     
  12. macrumors 68040

    lee1210

    Joined:
    Jan 10, 2005
    Location:
    Dallas, TX
    #12
    const is tricky.
    Code:
    const char *buffer[1024];
    That, however, is almost certainly not what was intended. if you are using a fixed size, using a local variable rather than mallocing from the heap is fine. The code above, though, is off. It is a list of 1024 character pointers, none of which are to be modified(the pointers are not to be assigned to). You have 1024*sizeof(char *) bytes there, but they aren't doing what you'd like.

    You'll get the right length, so you won't have any overflow issues, and since recv just takes a void * it doesn't care, it'll stick the data in the 4096/8192 bytes buffer is pointing to.

    This still really doesn't help in getting the EDGE connection activated before using it. I looked around the SDK documentation and couldn't find anything on this. Can you trace system/library calls in the debugger with the emulator? If so, maybe you can run one of the other programs and see if it makes a special call to activate the EDGE connection.

    -Lee
     
  13. thread starter macrumors regular

    Joined:
    Jul 20, 2007
    #13
    Thanks for the ideas. I finally figured it out!

    You DO have to start the EDGE connection if it wasn't previously started by another program, but unfortunately, it's not in the official iPhone SDK.

    To get EDGE working all the time, you have to link to Message.framework which is in the PrivateFrameworks directory. Then you have to include the NetworkController.h file and run this code:

    Code:
    if (![[NetworkController sharedInstance] isEdgeUp]){
         [[NetworkController sharedInstance] keepEdgeUp];
         [[NetworkController sharedInstance] bringUpEdge];
    }
     
  14. macrumors 68040

    lee1210

    Joined:
    Jan 10, 2005
    Location:
    Dallas, TX
    #14
    It's unfortunate that there is not a supported way to do this. i would be careful, as they may change the names of these functions in a subsequent release of the SDK/frameworks. Especially considering that these functions have "Edge" in the name, and EDGE will no longer be the only wireless data service available to the iPhone once iPhone 3G is released.

    -Lee
     
  15. macrumors newbie

    Joined:
    Jul 31, 2008
    #15
    Getting the exact same problem, but on 3G. Didn't test with EDGE at all. NetworkController.h seem to have been removed with the current official 2.0 SDK. Anyone know how to solve this problem? I need something to kickstart the 3G network...
     
  16. thread starter macrumors regular

    Joined:
    Jul 20, 2007
    #16
    It was never in the official SDK. You gotta use the unofficial API's.

     
  17. macrumors newbie

    Joined:
    Jul 31, 2008
    #17
    Is there no way to do this properly with BSD sockets? There are apps on official SDK that seem to connect within a few seconds regardless of whether 3G is "dormant" or not. Is it because these apps are using Apple's CFNetwork instead or low-level BSD sockets?
     
  18. thread starter macrumors regular

    Joined:
    Jul 20, 2007
    #18
    the BSD Sockets just send and receive data over the network. They don't actually start and stop network connections for you. That's done by the OS, and in this case, Apple doesn't give developers control over it.

    As for the samples, the example WITap requires a WiFi connection, and the example Reachability only checks if you're connected to the net.

    ~Matt

     
  19. macrumors newbie

    Joined:
    Jul 31, 2008
    #19
    Thanks Matt, so the best route would be to use Apple's CFNetwork API?
     
  20. thread starter macrumors regular

    Joined:
    Jul 20, 2007
    #20
    I would use the BSD sockets for maximum performance, and battery life. Starting the network connection using NetworkController only takes 2 lines of code.

     
  21. macrumors newbie

    Joined:
    Jul 31, 2008
    #21
    Can you elaborate on how to do that? (I don't have the header, hence i don't know what funcs are available...) You mentioned that NetworkController is in the unofficial SDK. I do not have that, can you kindly point me to somewhere where i could d/l that.

    Thanks for helping.
     
  22. thread starter macrumors regular

    Joined:
    Jul 20, 2007
    #22
    Create a new Header file by going into File->New File->C->Header File. Call it NetworkController.h

    Paste this in:

    Code:
    #import "NSObject.h" 
    
    @class NSString, NSTimer;
     
    @interface NetworkController : NSObject {
         struct __SCDynamicStore *_store;
         NSString *_domainName;
         unsigned int _waitingForDialToFinish:1;
         unsigned int _checkedNetwork:1;
         unsigned int _isNetworkUp:1;
         unsigned int _isFatPipe:1;
         unsigned int _edgeRequested:1;
         NSTimer *_notificationTimer;
    }
     
     + (id)sharedInstance;
     - (void)dealloc;
     - (id)init;
     - (BOOL)isNetworkUp;
     - (BOOL)isFatPipe;
     - (id)domainName;
     - (BOOL)isHostReachable:(id)fp8;
     - (id)primaryEthernetAddressAsString;
     - (void)registerCTServerRunLoopSource;
     - (id)IMEI;
     - (id)edgeInterfaceName;
     - (BOOL)isEdgeUp;
     - (void)bringUpEdge;
     - (void)keepEdgeUp;
    
    @end
    Then make sure you link against the Message.framework by right clicking on your Target, going to Info, pressing Add, and selecting message.framework.

    Then do:

    Code:
    [[NetworkController sharedInstance] keepEdgeUp];
    [[NetworkController sharedInstance] bringEdgeUp];
    
     
  23. macrumors newbie

    Joined:
    Jul 31, 2008
    #23
    Million thanks Matt!
     

Share This Page