Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Problem with creating sockets using CFSocket in Objective-C (iPhone app)

Ok, I have a problem with building a socket using Objective-C. If you'll take a look at my code below, through help with example code and other sources, I was able to build a complete socket I believe. The problem is that when I compile it, it builds fine (no syntax problems), but there are no sockets being created. As you'll notice I've commented out a lot of things in Server2.m and have isolated the problem to the very beginning when I create the struct for the listeningSocket. By the way, if this helps, it is part of the the server side of server-client application. Does anyone know why I would be getting this problem? Everything seemed to be working fine yesterday, and this morning I thought I would take a different approach to building the sockets, so I tried this. Thanks for any help!

Server_TrialViewController.m

#include <CFNetwork/CFSocketStream.h>
#import <UIKit/UIKit.h>
#import "Server2.h"
#import "Client_Test.h"

@interface Server_TrialViewController : UIViewController {
    IBOutlet UIButton *ServerButton;
    IBOutlet UIButton *ClientButton;
    IBOutlet UILabel *statusLabel;
    Server2 *server;
    Client_Test *client;
}

@property(nonatomic, retain) UILabel *statusLabel;
@property(nonatomic, retain) Server2 *server;
@property(nonatomic, retain) Client_Test *client;

-(IBAction)serverButtonPressed;
-(IBAction)clientButtonPressed;
//-(void)sendMessageWithServer:(Server_Test *)SERVER AndClient:(Client_Test *)CLIENT;

@end

Server_TrialViewController.h

#import "Server_TrialViewController.h"

@implementation Server_TrialViewController
@synthesize statusLabel;
@synthesize server;
@synthesize client;

-(IBAction)serverButtonPressed {
    if ([server start]) {
        [statusLabel setText:@"Success"];
    }
    else {
        if (server.status == NULL) {
            [statusLabel setText: @"No Server: No statUpdate"];
        }
        else {
            [statusLabel setText: @"No Server: Found statUpdate"];
        }
    }       
}


-(IBAction)clientButtonPressed {
    if ([client start]) {
        [statusLabel setText:@"Client Started"];
    }
    else {
        [statusLabel setText:@"Client Not Started"];
    }
}


- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning]; // Releases the view if it doesn't have a superview
    // Release anything that's not essential, such as cached data
}


- (void)dealloc {
    [super dealloc];
}

@end

Server2.h

#import <Foundation/Foundation.h>
#import "Server2Delegate.h"

@interface Server2 : NSObject
{
    uint16_t port;
    CFSocketRef listeningSocket;
    id<Server2Delegate> delegate;
    NSNetService* netService;
    NSString *status;
}

// Initialize connection
- (BOOL)start;
- (void)stop;

// Delegate receives various notifications about the state of our server
@property(nonatomic,retain) id<Server2Delegate> delegate;
@property(nonatomic, retain) NSString *status;

@end

Server2.m

#include <sys/socket.h>
#include <netinet/in.h>
#include <unistd.h>
#include <CFNetwork/CFSocketStream.h>

#import "Server2.h"
#import "Connection2.h"
#import "AppConfig2.h"

// Declare some private properties and methods
@interface Server2 ()
@property(nonatomic,assign) uint16_t port;
@property(nonatomic,retain) NSNetService* netService;

-(BOOL)createServer;
-(void)terminateServer;

@end

// Implementation of the Server interface
@implementation Server2

@synthesize delegate;
@synthesize port, netService;
@synthesize status;

// Cleanup
- (void)dealloc
{
    self.netService = nil;
    self.delegate = nil;
    [super dealloc];
}


// Create server and announce it
- (BOOL)start
{
    // Start the socket server
    if ( ! [self createServer] )
    {
        status = @"Server Not Created";
        return FALSE;
    }
    status = @"Server Created";
    return TRUE;
}


// Close everything
- (void)stop {
    [self terminateServer];
}

#pragma mark Callbacks

// Handle new connections
- (void)handleNewNativeSocket:(CFSocketNativeHandle)nativeSocketHandle {
    Connection2* connection = [[[Connection2 alloc] initWithNativeSocketHandle:nativeSocketHandle] autorelease];

    // In case of errors, close native socket handle
    if ( connection == nil ) {
        close(nativeSocketHandle);
        return;
    }

    // finish connecting
    if ( ! [connection connect] ) {
        //status = @"Connection Not Made";
        [connection close];
        return;
    }

    //status = @"Connection Made";

    // Pass this on to our delegate
    [delegate handleNewConnection:connection];
}


// This function will be used as a callback while creating our listening socket via 'CFSocketCreate'
static void serverAcceptCallback(CFSocketRef socket, CFSocketCallBackType type, CFDataRef address, const void *data, void *info) {
    Server2 *server = (Server2*)info;

    // We can only process "connection accepted" calls here
    if ( type != kCFSocketAcceptCallBack ) {
        return;
    }

    // for an AcceptCallBack, the data parameter is a pointer to a CFSocketNativeHandle
    CFSocketNativeHandle nativeSocketHandle = *(CFSocketNativeHandle*)data;

    [server handleNewNativeSocket:nativeSocketHandle];
}


#pragma mark Sockets and streams

- (BOOL)createServer
{
    //// PART 1: Create a socket that can accept connections

    // Socket context
    //  struct CFSocketContext {
    //   CFIndex version;
    //   void *info;
    //   CFAllocatorRetainCallBack retain;
    //   CFAllocatorReleaseCallBack release;
    //   CFAllocatorCopyDescriptionCallBack copyDescription;
    //  };
    CFSocketContext socketContext = {0, self, NULL, NULL, NULL};

    listeningSocket = CFSocketCreate(
                                     kCFAllocatorDefault,
                                     PF_INET,        // The protocol family for the socket
                                     SOCK_DGRAM,    // The socket type to create
                                     IPPROTO_UDP,    // The protocol for the socket. TCP vs UDP.
                                     0, //kCFSocketAcceptCallBack,  // New connections will be automatically accepted and the callback is called with the data argument being a pointer to a CFSocketNativeHandle of the child socket.
                                     NULL, //(CFSocketCallBack)&serverAcceptCallback,
                                     &socketContext );

    // Previous call might have failed
    if ( listeningSocket == NULL ) {
        status = @"listeningSocket Not Created";
        return FALSE;
    }
    else {
        status = @"listeningSocket Created";
        return TRUE;
    }
}
    /*
    // getsockopt will return existing socket option value via this variable
    int existingValue = 1;

    // Make sure that same listening socket address gets reused after every connection
    setsockopt( CFSocketGetNative(listeningSocket),
               SOL_SOCKET, SO_REUSEADDR, (void *)&existingValue,
               sizeof(existingValue));


    //// PART 2: Bind our socket to an endpoint.
    // We will be listening on all available interfaces/addresses.
    // Port will be assigned automatically by kernel.
    struct sockaddr_in socketAddress;
    memset(&socketAddress, 0, sizeof(socketAddress));
    socketAddress.sin_len = sizeof(socketAddress);
    socketAddress.sin_family = AF_INET;   // Address family (IPv4 vs IPv6)
    socketAddress.sin_port = 0;           // Actual port will get assigned automatically by kernel
    socketAddress.sin_addr.s_addr = htonl(INADDR_ANY);    // We must use "network byte order" format (big-endian) for the value here

    // Convert the endpoint data structure into something that CFSocket can use
    NSData *socketAddressData =
    [NSData dataWithBytes:&socketAddress length:sizeof(socketAddress)];

    // Bind our socket to the endpoint. Check if successful.
    if ( CFSocketSetAddress(listeningSocket, (CFDataRef)socketAddressData) != kCFSocketSuccess ) {
        // Cleanup
        if ( listeningSocket != NULL ) {
            status = @"Socket Not Binded";
            CFRelease(listeningSocket);
            listeningSocket = NULL;
        }

        return FALSE;
    }
    status = @"Socket Binded";

    //// PART 3: Find out what port kernel assigned to our socket
    // We need it to advertise our service via Bonjour
    NSData *socketAddressActualData = [(NSData *)CFSocketCopyAddress(listeningSocket) autorelease];

    // Convert socket data into a usable structure
    struct sockaddr_in socketAddressActual;
    memcpy(&socketAddressActual, [socketAddressActualData bytes],
           [socketAddressActualData length]);

    self.port = ntohs(socketAddressActual.sin_port);

    //// PART 4: Hook up our socket to the current run loop
    CFRunLoopRef currentRunLoop = CFRunLoopGetCurrent();
    CFRunLoopSourceRef runLoopSource = CFSocketCreateRunLoopSource(kCFAllocatorDefault, listeningSocket, 0);
    CFRunLoopAddSource(currentRunLoop, runLoopSource, kCFRunLoopCommonModes);
    CFRelease(runLoopSource);

    return TRUE;
}
*/

- (void) terminateServer {
    if ( listeningSocket != nil ) {
        CFSocketInvalidate(listeningSocket);
        CFRelease(listeningSocket);
        listeningSocket = nil;
    }
}


#pragma mark -
#pragma mark NSNetService Delegate Method Implementations

// Delegate method, called by NSNetService in case service publishing fails for whatever reason
- (void)netService:(NSNetService*)sender didNotPublish:(NSDictionary*)errorDict {
    if ( sender != self.netService ) {
        return;
    }

    // Stop socket server
    [self terminateServer];
}

@end
like image 609
Josh Bradley Avatar asked Jun 24 '09 19:06

Josh Bradley


2 Answers

For people looking for information about CFSocket server here's the answer: The code above is working fine if you change "SOCK_DGRAM" to "SOCK_STREAM".

like image 97
Leo Avatar answered Sep 27 '22 18:09

Leo


Have you tried setting kCFSocketAcceptCallBack to something other than 0?

If you're interested in socket programming on Mac OS X or the iPhone, I suggest you look at this example from Apple's documentation.

like image 23
Mike Avatar answered Sep 27 '22 19:09

Mike