views:

1458

answers:

2

I'm following the sample code in CFNetwork Programming Guide, specifically the section on Preventing Blocking When Working with Streams. my code is nearly identical to theirs (below) but, when I connect to my server, I get posix error 14 (bad address -- is that bad IP address (except it's not)? Bad memory address for some call I made? or what?!.

I have no idea how to go about debugging this. I'm really pretty new to the whole CFNetworking thing, and was never particularly expert at networks in the first place (the one thing I really loved about Java: easy networks! :D)

Anyway, log follows, with code below. Any hints would be greatly appreciated.

Log:

[6824:20b] [DEBUG] Compat version: 30000011
[6824:20b] [DEBUG] resovled host.
[6824:20b] [DEBUG] writestream opened.
[6824:20b] [DEBUG] readstream client assigned.
[6824:20b] [DEBUG] readstream opened.
[6824:20b] [DEBUG] *** Read stream reported kCFStreamEventErrorOccurred
[6824:20b] [DEBUG] *** POSIX error: 14 - Bad address
[6824:20b] Error closing readstream
[6824:20b] [DEBUG] Writing int: 0x09000000 (0x00000009)

Code:

+ (BOOL) connectToServerNamed:(NSString*)name atPort:(int)port {
    CFHostRef theHost = CFHostCreateWithName (NULL, (CFStringRef)name);
    CFStreamError error;

    if (CFHostStartInfoResolution (theHost, kCFHostReachability, &error))
    {
        NSLog (@"[DEBUG] resovled host.");
        CFStreamCreatePairWithSocketToCFHost (NULL, theHost, port, &readStream, &writeStream);
        if (CFWriteStreamOpen(writeStream))
        {
            NSLog (@"[DEBUG] writestream opened.");

            CFStreamClientContext myContext = { 0, self, NULL, NULL, NULL };
            CFOptionFlags registeredEvents = kCFStreamEventHasBytesAvailable |
                    kCFStreamEventErrorOccurred | kCFStreamEventEndEncountered;
            if (CFReadStreamSetClient (readStream, registeredEvents, readCallBack, &myContext))
            {
                NSLog (@"[DEBUG] readstream client assigned.");
                CFReadStreamScheduleWithRunLoop(readStream, CFRunLoopGetCurrent(),
                                                kCFRunLoopCommonModes);
                if (CFReadStreamOpen(readStream))
                {
                    NSLog (@"[DEBUG] readstream opened.");
                    CFRunLoopRun();
         // Lots of error condition handling snipped.
        [...]
    return YES;
}


void readCallBack (CFReadStreamRef stream, CFStreamEventType event, void *myPtr)
{
    switch (event)
    {
        case kCFStreamEventHasBytesAvailable:
        {
            CFIndex bytesRead = CFReadStreamRead(stream, buffer, kNetworkyBitsBufferSize); // won't block
            if (bytesRead > 0)                                                  // <= 0 leads to additional events
            {
                if (listener)
                {
                    UInt8 *tmpBuffer = malloc (sizeof (UInt8) * bytesRead);
                    memcpy (buffer, tmpBuffer, bytesRead);
                    NSLog(@"[DEBUG] reveived %d bytes", bytesRead);
                    [listener networkDataArrived:tmpBuffer count:bytesRead];
                }
                NSLog(@"[DEBUG] reveived %d bytes; no listener", bytesRead);
            }
        }
            break;

        case kCFStreamEventErrorOccurred:
            NSLog(@"[DEBUG] *** Read stream reported kCFStreamEventErrorOccurred");
            CFStreamError error = CFReadStreamGetError(stream);
            logError(error);
            [NetworkyBits shutDownRead];
            break;

        case kCFStreamEventEndEncountered:
            NSLog(@"[DEBUG] *** Read stream reported kCFStreamEventEndEncountered");
            [NetworkyBits shutDownRead];
            break;
    }
}

void logError (CFStreamError error)
{
    if (error.domain == kCFStreamErrorDomainPOSIX)                              // Interpret error.error as a UNIX errno.
    {
        NSLog (@"[DEBUG] *** POSIX error: %d - %s", (int) error.error, strerror(error.error));
    }
    else if (error.domain == kCFStreamErrorDomainMacOSStatus)
    {
        NSLog (@"[DEBUG] *** MacOS error: %d", (int) error.error);
    }
    else
    {
        NSLog (@"[DEBUG] *** Stream error domain: %d, error: %d", (int) error.error);
    }
}
A: 

I'm not familiar with Cocoa or Objective-C, but I can tell you that POSIX error code 14 is called EFAULT, and it means you made a system call with an invalid pointer value. This is almost certainly some user-supplied buffer pointer to a read or write system call of some sort. Check all of the buffer pointers and make sure they're not NULL. Check the return value from malloc() - you might be failing to allocate a buffer.

Adam Rosenfield
@Adam, NULL wouldn't cause EFAULT, it would cause a bus error or segv signal. Somehow his read buffer must be getting deallocated or initialized as meaningless stack junk.
Jason Coco
Some processors (possibly PowerPC, I don't know off the top of my head) disallow references except on specific boundaries which is generally what causes EFAULT (if you dereference a 4-byte word on a non-4-byte boundary, that causes this sort of behavior).
paxdiablo
@Pax, PowerPC will produce a bus error on non-aligned structures, but this issue for read is definitely just a bad (uninitialized or deallocated) buffer.
Jason Coco
+1  A: 

Olie, where does

buffer

that you supply to

CFReadStreamRead()

come from? EFAULT is a bad buffer address... are you sure you've actually initialized this buffer to point to something valid? It's obviously a global or sometime... which itself is a pretty bad idea. You should allocate it in your function or it should be an ivar (if you're using Obj-C).

Jason Coco
I don't think it's getting to that code. Based on the debug output, it appears to be selecting the kCFStreamEventErrorOccurred case of the switch statement.
paxdiablo
@Pax, it is... the error happens inside of CFReadStreamRead because the buffer is bad, it then calls the call-back again with the kCFStreamEventErrorOccurred event. If you put a printf at the top of the function you'll see it will be called twice, once for the read and then again for the error.
Jason Coco
I stand corrected - it didn't occur to me that the callback could be called from the callback, so to speak, but it makes sense now looking at the case statements (first for bytes available, second for reading those bytes into the dodgy buffer).
paxdiablo
Yeah, he could have used different callback functions for different events, but he chose to stick them all in the one so it can be a little confusing :) Any error that happens on that read stream will call back into that function, even if it happens on another thread...
Jason Coco
Bah! You are right, Jason. I turns out that I understand networking better than I thought, I just can't code. <sigh> uninitialized buffer and all was fine.Apologies for the dumb "debug my code" question...
Olie
Btw, I also took your [indirect] advice and changed the whole thing from a singleton (with static buffer) to an object (never know: you might want to have multiple connections -- duh!) so buffer (and everything else) is now an ivar.
Olie
@Olie, Yeah, ivars work well because (unlike static variables) they automatically get initialized to 0 which makes debugging a lot easier because you'll get a protection fault when you try to access them.
Jason Coco