Using NSFileHandleConnectionAcceptedNotification for a server process

  • I'm trying to build a simple server and I'm using NSFileHandle to read
    the incoming requests. It works, more or less, but what I have run
    into is strange. In my call back when I read from the file handle I
    always only get the first 510 bytes. I tried to use

    int maxBuf=4096;
    setsockopt(fd, SOL_SOCKET, SO_RCVBUF, (void *)&maxBuf, sizeof(maxBuf));

    before binding and registering for
    NSFileHandleConnectionAcceptedNotification but I still only get 510
    bytes. I tried other larger and smaller values but the result is
    always the same. I could keep queuing up calls with
    NSFileHandleReadCompletionNotification but how will I know when I'm
    done getting the data I am expecting ? I'm expecting a complete xml
    record that I'm going to read process and reply to. I could go back
    and retread one written in C but that wouldn't be any fun.

    Any ideas how I can get it to feed me more upfront ?
    Matt

    snippet:

            int fd = -1;
            CFSocketRef socket;
            socket = CFSocketCreate(kCFAllocatorDefault, PF_INET,
    SOCK_STREAM, IPPROTO_TCP, 0, NULL, NULL);
            if( socket ) {
                fd = CFSocketGetNative(socket);
                int yes = 1;
      int maxBuf = 4096;
      int err = 0;
                err = setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (void
    *)&yes, sizeof(yes));
                err = setsockopt(fd, SOL_SOCKET, SO_RCVBUF, (void
    *)&maxBuf, sizeof(maxBuf));

                struct sockaddr_in addr4;
                memset(&addr4, 0, sizeof(addr4));
                addr4.sin_len = sizeof(addr4);
                addr4.sin_family = AF_INET;
                addr4.sin_port = htons(port);
                addr4.sin_addr.s_addr = htonl(INADDR_ANY);
                NSData *address4 = [NSData dataWithBytes:&addr4
    length:sizeof(addr4)];
                if (kCFSocketSuccess != CFSocketSetAddress(socket,
    (CFDataRef)address4)) {
                    NSLog(@"Could not bind to address");
                }
            } else {
                NSLog(@"No server socket");
            }

            fileHandle = [[NSFileHandle alloc] initWithFileDescriptor:fd
                                                        closeOnDealloc:YES];

            NSNotificationCenter *nc = [NSNotificationCenter
    defaultCenter];
            [nc addObserver:self
                    selector:@selector(newConnection:)
                        name:NSFileHandleConnectionAcceptedNotification
                      object:nil];

            [fileHandle acceptConnectionInBackgroundAndNotify];
        }
  • On 8 Feb '08, at 1:56 PM, Matt Mashyna wrote:

    > I'm trying to build a simple server and I'm using NSFileHandle to
    > read the incoming requests. It works, more or less, but what I have
    > run into is strange. In my call back when I read from the file
    > handle I always only get the first 510 bytes.

    The code you posted accepts new connections, but you didn't show the
    code that reads data from a new connection. So I have to guess about
    how it works ... one common gotcha is that -readInBackgroundAndNotify:
    is a one-shot — it will only notify you once. In that notification
    callback, you have to call it again to register for the next bunch of
    data. If you don't re-register, the effect is like what you're
    reporting.

    —Jens
  • On Feb 8, 2008, at 5:07 PM, Jens Alfke wrote:

    >
    > On 8 Feb '08, at 1:56 PM, Matt Mashyna wrote:
    >
    >> I'm trying to build a simple server and I'm using NSFileHandle to
    >> read the incoming requests. It works, more or less, but what I have
    >> run into is strange. In my call back when I read from the file
    >> handle I always only get the first 510 bytes.
    >
    > The code you posted accepts new connections, but you didn't show the
    > code that reads data from a new connection. So I have to guess about
    > how it works ... one common gotcha is that -
    > readInBackgroundAndNotify: is a one-shot — it will only notify you
    > once. In that notification callback, you have to call it again to
    > register for the next bunch of data. If you don't re-register, the
    > effect is like what you're reporting.
    >
    > —Jens

    Well, I guess I was stuck on the idea that my problem was really that
    I needed to increase the fd buffer so I would get the whole chunk of
    data at one time but maybe I'm not thinking about it in the right way.
    I can re-register for more data but then it's not clear to me how I
    know that the client is done sending it's stream and waiting for a
    reply. There doesn't seem to be a NSFileHandle method to see if more
    data is waiting to be read. I know I'm being dense. I just need a slap!

    Here's where it gets the connect notification:

    - (void)newConnection:(NSNotification *)notification
    {
        NSDictionary *userInfo = [notification userInfo];
        NSFileHandle *remoteFileHandle = [userInfo objectForKey:
    NSFileHandleNotificationFileHandleItem];
        NSNumber *errorNo = [userInfo objectForKey:@"NSFileHandleError"];
        if( errorNo ) {
            NSLog(@"NSFileHandle Error: %@", errorNo);
            return;
        }

            [[NSNotificationCenter defaultCenter] addObserver:self
                    selector:@selector(dataReceivedNotification:)
                        name:NSFileHandleReadCompletionNotification
                      object:remoteFileHandle];

            [remoteFileHandle readInBackgroundAndNotify];

    } // newConnection

    And here's where I actually read it... 510 bytes or less, every time!

    - (void)dataReceivedNotification:(NSNotification *)notification
    {
    NSData *data = [[notification userInfo]
    objectForKey:NSFileHandleNotificationDataItem];
    NSString* foo = [[NSString alloc] initWithData:data
    encoding:NSASCIIStringEncoding];
    NSLog(foo);
    [foo release];

    // do stuff with the data now
    } // dataReceivedNotification
  • On 8 Feb '08, at 5:14 PM, Matt Mashyna wrote:

    > Well, I guess I was stuck on the idea that my problem was really
    > that I needed to increase the fd buffer so I would get the whole
    > chunk of data at one time but maybe I'm not thinking about it in the
    > right way.

    I think you could get that effect by calling -
    readToEndOfFileInBackgroundAndNotify instead; but I've never tried
    that. In generally it's better to read the data in chunks, as that way
    you can provide progress feedback to the user. It also avoids loading
    all of the data into memory at once.

    > I can re-register for more data but then it's not clear to me how I
    > know that the client is done sending it's stream and waiting for a
    > reply. There doesn't seem to be a NSFileHandle method to see if more
    > data is waiting to be read. I know I'm being dense. I just need a
    > slap!

    IIRC, what happens at EOF is that you get a notification whose NSData
    payload is either zero bytes long or nil (not sure which). So the
    logic is that, if you received at least one byte of data, you call -
    readInBackgroundAndNotify again, else you know you're done.

    Don't feel bad about being confused about this. The docs are pretty
    vague, and I got really confused when I was first trying to use
    NSFileHandle too.

    —Jens