Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is it possible to use a GCD dispatch source to do an async connect() on a socket?

You can use GCD dispatch sources to read and write from sockets, monitor a listening socket for incoming connections, but I couldn't figure out how to also use a dispatch source to connect a socket?

In pseudo-code, it would look something like this:

dispatch_source_t source = dispatch_source_create(DISPATCH_SOURCE_TYPE_WRITE, connectingSocket, ...);
dispatch_source_set_event_handler(source, ^{
  // Socket did connect or not
});
fcntl(connectingSocket, F_SETFL, O_NONBLOCK);
connect(connectingSocket, addr, len);
dispatch_source_resume(source);

This would be nicer than having to use select().

like image 443
Pol Avatar asked Oct 15 '14 17:10

Pol


1 Answers

I initially mis-parsed your question... sorry. I get it now... you want to get EINPROGRESS from connect and have a dispatch source notify you when the connect call needs attention instead of polling with select... This was fairly easy to hack up, and appears to work:

#import <sys/types.h>
#import <sys/socket.h>
#import <netinet/in.h>
#import <arpa/inet.h>

@implementation AppDelegate
{
    dispatch_source_t foo;
}

- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {

    int socketFD = socket(PF_INET, SOCK_STREAM, 0);
    if (socketFD == -1)
    {
        socketFD = -1;
        abort();
    }

    int flags = fcntl(socketFD, F_GETFL, 0);
    int status = fcntl(socketFD, F_SETFL, flags | O_NONBLOCK);
    if (status == -1)
    {
        close(socketFD);
        socketFD = -1;
        abort();
    }

    struct sockaddr_in sockaddr4 = {0};
    sockaddr4.sin_len         = sizeof(sockaddr4);
    sockaddr4.sin_family      = AF_INET;
    sockaddr4.sin_port        = htons(22);
    inet_aton("127.0.0.1", &sockaddr4.sin_addr);

    foo = dispatch_source_create(DISPATCH_SOURCE_TYPE_WRITE, socketFD, 0, dispatch_get_main_queue());
    dispatch_source_set_event_handler(foo, ^{
        if (connect(socketFD, (const struct sockaddr *)&sockaddr4, (socklen_t)sizeof(sockaddr4)))
        {
            int err = errno;
            NSLog(@"errno: %s", strerror(err));
            if (err == ECONNREFUSED)
            {
                abort();
            }
            else if (err == EISCONN)
            {
                // connected -- queue up other work
                DoStuff();

                // Cancel the source so it doesnt keep notifying...
                dispatch_source_cancel(foo);
            }
        }
    });

    dispatch_source_set_cancel_handler(foo, ^{
        NSLog(@"Cancel");
    });

    dispatch_resume(foo);

    // Do initial connect
    if (connect(socketFD, (const struct sockaddr *)&sockaddr4, (socklen_t)sizeof(sockaddr4)))
    {
        if(errno != EINPROGRESS)
        {
            close(socketFD);
            socketFD = -1;
            abort();
        }
    }
}

@end
like image 117
ipmcc Avatar answered Oct 20 '22 17:10

ipmcc